[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/workflows/dotnet.yml",
    "content": "name: Build App\n\non: [ push, pull_request ]\n\njobs:\n\n  build:\n\n    runs-on: windows-latest\n\n    steps:\n      - name: Inject slug/short variables\n        uses: rlespinasse/github-slug-action@v3.x\n\n      - name: Get current date\n        id: date\n        run: echo \"::set-output name=date::$(date +'%Y-%m-%d')\"\n\n      - name: Set version string\n        id: ver\n        run: echo \"::set-output name=ver::DiztinGUIsh-${{ steps.date.outputs.date }}--${{ env.GITHUB_REF_SLUG }}-${{ env.GITHUB_SHA_SHORT }}-${{env.GITHUB_REPOSITORY_OWNER_PART_SLUG != 'isofrieze' && env.GITHUB_REPOSITORY_OWNER_PART_SLUG || 'official'}}\"\n\n      - name: Print version\n        run: echo \"version ${{ steps.ver.outputs.ver }}\"\n\n      - name: Checkout\n        uses: actions/checkout@v2\n        with:\n          submodules: 'recursive'\n          fetch-depth: 0  # Fetch all history for all tags\n\n      - name: Auto-bump version and create tag on release branch\n        if: github.ref == 'refs/heads/release' && github.event_name == 'push'\n        run: |\n          # Get the latest tag\n          $latestTag = git describe --tags --abbrev=0 2>$null\n          if (-not $latestTag) {\n            $latestTag = \"v0.0.0.0\"\n          }\n          \n          Write-Host \"Latest tag: $latestTag\"\n          \n          # Extract version numbers (remove 'v' prefix)\n          $version = $latestTag.Substring(1)\n          $versionParts = $version.Split('.')\n          \n          # Ensure we have 4 parts (major.minor.patch.build)\n          while ($versionParts.Length -lt 4) {\n            $versionParts += \"0\"\n          }\n          \n          # Bump the patch version (third number) and reset build to 0\n          $major = [int]$versionParts[0]\n          $minor = [int]$versionParts[1]\n          $patch = [int]$versionParts[2] + 1\n          $build = 0\n          \n          $newVersion = \"$major.$minor.$patch.$build\"\n          $newTag = \"v$newVersion\"\n          \n          Write-Host \"New tag: $newTag\"\n          \n          # Configure git\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"github-actions[bot]@users.noreply.github.com\"\n          \n          # Create and push the new tag\n          git tag $newTag\n          git push origin $newTag\n        shell: powershell\n\n      - name: Setup dotnet\n        uses: actions/setup-dotnet@v1\n        with:\n          dotnet-version: 9.0.x\n      \n      # not currently working consistently, don't cache\n      #    - name: Nuget (restore from cache)\n      #      uses: actions/cache@v1\n      #      id: cache\n      #      with:\n      #        path: ~/.nuget/packages\n      #        key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}\n      #    \n      #    - name: NuGet (Restore from scratch if needed)\n      #      if: steps.cache.outputs.cache-hit != 'true'\n      #      run: dotnet restore --locked-mode\n\n      - name: NuGet (Restore always from scratch)\n        run: dotnet restore --locked-mode\n\n      - name: Build\n        run: dotnet build --no-restore --configuration Release\n\n      - name: Test\n        run: dotnet test --no-build --configuration Release\n\n      # removed --no-restore on this for simplicity. it's a little more wasteful potentially though\n      - name: Publish\n        run: dotnet publish -c Release -r win-x64  --self-contained true /p:PublishProfile=FolderProfile /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true .\\Diz.App.Winforms\n\n      - uses: vimtor/action-zip@v1.2\n        with:\n          files: Diz.App.Winforms\\bin\\Release\\net9.0-windows\\publish\\\n          dest: ${{ steps.ver.outputs.ver }}.zip\n\n      - name: Upload Artifact\n        uses: actions/upload-artifact@v4\n        with:\n          name: ${{ steps.ver.outputs.ver }}.zip\n          path: ${{ steps.ver.outputs.ver }}.zip\n\n      - name: Release\n        uses: softprops/action-gh-release@v1\n        if: startsWith(github.ref, 'refs/tags/')\n        with:\n          files: ${{ steps.ver.outputs.ver }}.zip\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "# CUSTOM STUFF\nDiztinGUIsh/test/\ndiztinguish.zip\nact.exe\n\n## 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\n\n\n\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n.idea/**/workspace.xml\n.idea/**/tasks.xml\n.idea/**/usage.statistics.xml\n.idea/**/dictionaries\n.idea/**/shelf\n\n# AWS User-specific\n.idea/**/aws.xml\n\n# Generated files\n.idea/**/contentModel.xml\n\n# Sensitive or high-churn files\n.idea/**/dataSources/\n.idea/**/dataSources.ids\n.idea/**/dataSources.local.xml\n.idea/**/sqlDataSources.xml\n.idea/**/dynamic.xml\n.idea/**/uiDesigner.xml\n.idea/**/dbnavigator.xml\n\n# Gradle\n.idea/**/gradle.xml\n.idea/**/libraries\n\n# Gradle and Maven with auto-import\n# When using Gradle or Maven with auto-import, you should exclude module files,\n# since they will be recreated, and may cause churn.  Uncomment if using\n# auto-import.\n# .idea/artifacts\n# .idea/compiler.xml\n# .idea/jarRepositories.xml\n# .idea/modules.xml\n# .idea/*.iml\n# .idea/modules\n# *.iml\n# *.ipr\n\n# CMake\ncmake-build-*/\n\n# Mongo Explorer plugin\n.idea/**/mongoSettings.xml\n\n# File-based project format\n*.iws\n\n# IntelliJ\nout/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Cursive Clojure plugin\n.idea/replstate.xml\n\n# SonarLint plugin\n.idea/sonarlint/\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n# Editor-based Rest Client\n.idea/httpRequests\n\n# Android studio 3.1+ serialized cache file\n.idea/caches/build_file_checksums.ser"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"Diz.LogWriter\"]\n\tpath = Diz.LogWriter\n\turl = https://github.com/DizTools/Diz.LogWriter\n[submodule \"Diz.Ui.Winforms\"]\n\tpath = Diz.Ui.Winforms\n\turl = https://github.com/DizTools/Diz.Ui.Winforms\n"
  },
  {
    "path": ".idea/.idea.DiztinGUIsh/.idea/.gitignore",
    "content": "﻿# Default ignored files\n/shelf/\n/workspace.xml\n# Rider ignored files\n/modules.xml\n/contentModel.xml\n/.idea.DiztinGUIsh.iml\n/projectSettingsUpdater.xml\n# Editor-based HTTP Client requests\n/httpRequests/\n# Datasource local storage ignored files\n/dataSources/\n/dataSources.local.xml\n"
  },
  {
    "path": ".idea/.idea.DiztinGUIsh/.idea/.name",
    "content": "DiztinGUIsh"
  },
  {
    "path": ".idea/.idea.DiztinGUIsh/.idea/encodings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\" addBOMForNewFiles=\"with BOM under Windows, with no BOM otherwise\" />\n</project>"
  },
  {
    "path": ".idea/.idea.DiztinGUIsh/.idea/indexLayout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"UserContentModel\">\n    <attachedFolders />\n    <explicitIncludes />\n    <explicitExcludes />\n  </component>\n</project>"
  },
  {
    "path": ".idea/.idea.DiztinGUIsh/.idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$/Diz.LogWriter\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$/Diz.Ui.Winforms\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": ".run/Diz.Ui.Eto (Experimental GUI, don't use).run.xml",
    "content": "﻿<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Diz.Ui.Eto (Experimental GUI, don't use)\" type=\"DotNetProject\" factoryName=\".NET Project\">\n    <option name=\"EXE_PATH\" value=\"$PROJECT_DIR$/Diz.App.Eto/bin/Debug/net9.0-windows/Diz.App.Eto.exe\" />\n    <option name=\"PROGRAM_PARAMETERS\" value=\"\" />\n    <option name=\"WORKING_DIRECTORY\" value=\"$PROJECT_DIR$/Diz.App.Eto/bin/Debug/net9.0-windows\" />\n    <option name=\"PASS_PARENT_ENVS\" value=\"1\" />\n    <option name=\"USE_EXTERNAL_CONSOLE\" value=\"0\" />\n    <option name=\"USE_MONO\" value=\"0\" />\n    <option name=\"RUNTIME_ARGUMENTS\" value=\"\" />\n    <option name=\"PROJECT_PATH\" value=\"$PROJECT_DIR$/Diz.App.Eto/Diz.App.Eto.csproj\" />\n    <option name=\"PROJECT_EXE_PATH_TRACKING\" value=\"1\" />\n    <option name=\"PROJECT_ARGUMENTS_TRACKING\" value=\"1\" />\n    <option name=\"PROJECT_WORKING_DIRECTORY_TRACKING\" value=\"1\" />\n    <option name=\"PROJECT_KIND\" value=\"DotNetCore\" />\n    <option name=\"PROJECT_TFM\" value=\"net9.0-windows\" />\n    <method v=\"2\">\n      <option name=\"Build\" />\n    </method>\n  </configuration>\n</component>"
  },
  {
    "path": ".run/DiztinGUIsh - WinForms (USE THIS).run.xml",
    "content": "﻿<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"DiztinGUIsh - WinForms (USE THIS)\" type=\"DotNetProject\" factoryName=\".NET Project\">\n    <option name=\"EXE_PATH\" value=\"$PROJECT_DIR$/Diz.App.Winforms/bin/Debug/net9.0-windows/win-x64/Diz.App.Winforms.exe\" />\n    <option name=\"PROGRAM_PARAMETERS\" value=\"\" />\n    <option name=\"WORKING_DIRECTORY\" value=\"$PROJECT_DIR$/Diz.App.Winforms/bin/Debug/net9.0-windows/win-x64\" />\n    <option name=\"PASS_PARENT_ENVS\" value=\"1\" />\n    <option name=\"USE_EXTERNAL_CONSOLE\" value=\"0\" />\n    <option name=\"ENV_FILE_PATHS\" value=\"\" />\n    <option name=\"REDIRECT_INPUT_PATH\" value=\"\" />\n    <option name=\"PTY_MODE\" value=\"Auto\" />\n    <option name=\"USE_MONO\" value=\"0\" />\n    <option name=\"RUNTIME_ARGUMENTS\" value=\"\" />\n    <option name=\"AUTO_ATTACH_CHILDREN\" value=\"0\" />\n    <option name=\"MIXED_MODE_DEBUG\" value=\"0\" />\n    <option name=\"PROJECT_PATH\" value=\"$PROJECT_DIR$/Diz.App.Winforms/Diz.App.Winforms.csproj\" />\n    <option name=\"PROJECT_EXE_PATH_TRACKING\" value=\"1\" />\n    <option name=\"PROJECT_ARGUMENTS_TRACKING\" value=\"1\" />\n    <option name=\"PROJECT_WORKING_DIRECTORY_TRACKING\" value=\"1\" />\n    <option name=\"PROJECT_KIND\" value=\"DotNetCore\" />\n    <option name=\"PROJECT_TFM\" value=\"net9.0-windows\" />\n    <method v=\"2\">\n      <option name=\"Build\" />\n    </method>\n  </configuration>\n</component>"
  },
  {
    "path": "Directory.Build.targets",
    "content": "<Project>\n    <PropertyGroup>\n        <DefineConstants>$(DefineConstants)</DefineConstants>\n    </PropertyGroup>\n    <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\n        <DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>\n    </PropertyGroup>\n</Project>"
  },
  {
    "path": "Diz.App.Common/Diz.App.Common.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <PackageReference Include=\"JetBrains.Annotations\">\n        <Version>2023.3.0</Version>\n      </PackageReference>\n      <PackageReference Include=\"LightInject\">\n        <Version>6.6.4</Version>\n      </PackageReference>\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\Diz.Controllers\\Diz.Controllers\\Diz.Controllers.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.App.Common/DizAppCompositionRoot.cs",
    "content": "﻿using Diz.Controllers.interfaces;\nusing Diz.Controllers.services;\nusing Diz.Core.services;\nusing Diz.Cpu._65816;\nusing Diz.Import;\nusing Diz.LogWriter.services;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.App.Common;\n\n[UsedImplicitly] public class DizAppCommonCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        // no platform-specific stuff in here.\n        // i.e. no winforms/QT/etc.\n        \n        // the (string) names of the views above will be mapped to the method names of the interface below.\n        // i.e. calling IViewFactory.GetLabelEditorView() will look for somthing named \"LabelEditorView\"\n        serviceRegistry.EnableAutoFactories();\n        \n        serviceRegistry.RegisterFrom<DizCoreServicesCompositionRoot>();\n        serviceRegistry.RegisterFrom<DizControllersCompositionRoot>();\n        serviceRegistry.RegisterFrom<DizCpu65816ServiceRoot>();\n        serviceRegistry.RegisterFrom<DizImportServiceRegistration>();\n        serviceRegistry.RegisterFrom<LogWriterServiceRegistration>();\n        \n        serviceRegistry.RegisterAutoFactory<IViewFactory>();\n    }\n}"
  },
  {
    "path": "Diz.App.Common/Main.cs",
    "content": "﻿using Diz.Controllers.interfaces;\nusing LightInject;\n\nnamespace Diz.App.Common;\n\npublic static class DizAppCommon\n{\n    public static void StartApp(IServiceFactory serviceFactory, string[] args)\n    {\n        // platform-independent app startup \n        var dizApp = serviceFactory.GetInstance<IDizApp>();\n        \n        var fileToOpen = \"\";\n        if (args.Length > 0)\n            fileToOpen = args[0];\n        \n        dizApp.Run(fileToOpen);\n    }\n}"
  },
  {
    "path": "Diz.App.Common/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.App.Common;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizAppCommonCompositionRoot))]"
  },
  {
    "path": "Diz.App.Eto/Diz.App.Eto.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <OutputType>WinExe</OutputType>\n        <TargetFramework>net9.0-windows</TargetFramework>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <PackageReference Include=\"JetBrains.Annotations\">\n        <Version>2023.3.0</Version>\n      </PackageReference>\n      <PackageReference Include=\"LightInject\">\n        <Version>6.6.4</Version>\n      </PackageReference>\n        <PackageReference Include=\"GitInfo\">\n            <Version>3.3.3</Version>\n            <PrivateAssets>all</PrivateAssets>\n            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n        </PackageReference>\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\Diz.App.Common\\Diz.App.Common.csproj\" />\n      <ProjectReference Include=\"..\\Diz.Core\\Diz.Core.csproj\" />\n      <ProjectReference Include=\"..\\Diz.Ui.Eto\\Diz.Ui.Eto.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.App.Eto/DizAppEtoCompositionRoot.cs",
    "content": "﻿using Diz.App.Common;\nusing Diz.Controllers.interfaces;\nusing Diz.Ui.Eto;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.App.Eto;\n\n[UsedImplicitly] public class DizAppEtoCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.RegisterFrom<DizAppCommonCompositionRoot>();\n        \n        serviceRegistry.Register<IDizApp, DizEtoApp>();\n        serviceRegistry.Register<ICommonGui, EtoCommonGui>();\n    }\n}"
  },
  {
    "path": "Diz.App.Eto/DizEtoApp.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Controllers.interfaces;\nusing Diz.Ui.Eto.ui;\nusing Eto.Forms;\n\nnamespace Diz.App.Eto;\n\npublic class DizEtoApp(IViewFactory viewFactory) : IDizApp\n{\n    public void Run(string initialProjectFileToOpen = \"\")\n    {\n        var application = new Application();\n        \n        var mainWindow = viewFactory.GetMainGridWindowView();\n\n        // janky casting\n        // ReSharper disable once SuspiciousTypeConversion.Global\n        var window = mainWindow as EtoMainGridForm;\n        Debug.Assert(window != null);\n        \n        if (initialProjectFileToOpen != \"\") {\n            window.ProjectController.OpenProject(initialProjectFileToOpen);\n        }\n\n        application.Run(window);\n    }\n}"
  },
  {
    "path": "Diz.App.Eto/Program.cs",
    "content": "﻿using Diz.App.Common;\n\nnamespace Diz.App.Eto;\n\ninternal static class Program\n{\n    [STAThread]\n    private static void Main(string[] args)\n    {\n        var serviceFactory = DizEtoRegisterServices.CreateServiceFactoryAndRegisterTypes();\n        DizAppCommon.StartApp(serviceFactory, args);\n    }\n}"
  },
  {
    "path": "Diz.App.Eto/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.App.Eto;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizAppEtoCompositionRoot))]"
  },
  {
    "path": "Diz.App.Eto/RegisterWinformsServices.cs",
    "content": "using Diz.App.Common;\nusing Diz.Core.util;\nusing Diz.Ui.Eto;\nusing LightInject;\n\nnamespace Diz.App.Eto;\n\npublic static class DizEtoRegisterServices\n{\n    public static IServiceFactory CreateServiceFactoryAndRegisterTypes()\n    {\n        var serviceProvider = DizServiceProvider.CreateServiceContainer();\n        RegisterDizUiServices(serviceProvider);\n        \n        return serviceProvider;\n    }\n\n    public static void RegisterDizUiServices(IServiceRegistry serviceRegistry)\n    {\n        // option #1: we can simply register services in any Diz*dll's that are found in a scan.\n        // this is easy but we have less control\n        // DizCoreServicesDllRegistration.RegisterServicesInDizDlls(serviceRegistry);\n\n        // option #2: register everything by hand (this is what we'll do).\n        \n        // pull in all common stuff (platform-independent)\n        serviceRegistry.RegisterFrom<DizAppCommonCompositionRoot>();\n        \n        // pull in winforms-specific UI stuff:\n        serviceRegistry.RegisterFrom<DizUiEtoCompositionRoot>();\n        \n        // finally, pull in OUR stuff, which is winforms-specific\n        serviceRegistry.RegisterFrom<DizAppEtoCompositionRoot>();\n    }\n}"
  },
  {
    "path": "Diz.App.PowerShell/Cmdlets.cs",
    "content": "﻿#nullable enable\n\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Management.Automation;\nusing Diz.Core;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.PowerShell;\n\n[UsedImplicitly]\n[Cmdlet(VerbsLifecycle.Build, \"AssemblyFiles\")]\npublic class BuildAssemblyFilesCmdlet : ServiceContainerCmdletBase\n{\n    [Parameter(Position = 0)]\n    [ValidateNotNullOrEmpty]\n    public string[]? ProjectNames { get; set; } = null;\n    \n    protected override void ProcessRecord()\n    {\n        if (ProjectNames == null)\n            return;\n\n        if (ProjectNames.Length <= 0)\n            return;\n\n        foreach (var projectName in ProjectNames)\n        {\n            BuildAssembly(projectName);\n        }\n    }\n\n    [SuppressMessage(\"ReSharper\", \"UnusedMethodReturnValue.Local\")]\n    private bool BuildAssembly(string projectFileName)\n    {\n        // this ONE TIME, this service locator anti-pattern is OK because we ARE the top-level class.\n        var projectFileAssemblyExporter = ServiceContainer.GetInstance<IProjectFileAssemblyExporter>();\n        Debug.Assert(projectFileAssemblyExporter != null);\n        return projectFileAssemblyExporter.ExportAssembly(projectFileName);\n    }\n\n    protected override void StopProcessing() {}\n    protected override void EndProcessing() {}\n}"
  },
  {
    "path": "Diz.App.PowerShell/Diz.App.PowerShell.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Library</OutputType>\n    <TargetFramework>net9.0</TargetFramework>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <GitThisAssemblyMetadata>true</GitThisAssemblyMetadata>\n    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>\n    <Nullable>enable</Nullable>\n    <RootNamespace>Diz.PowerShell</RootNamespace>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"GitInfo\" Version=\"3.3.3\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"LightInject\" Version=\"6.6.4\" />\n    <PackageReference Include=\"System.Management.Automation\" Version=\"7.4.1\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Diz.Core\\Diz.Core.csproj\" />\n    <ProjectReference Include=\"..\\Diz.LogWriter\\Diz.LogWriter.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.App.PowerShell/DizPowerShellCompositionRoot.cs",
    "content": "﻿using Diz.Core;\nusing LightInject;\n\nnamespace Diz.PowerShell;\n\npublic class DizPowerShellCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        // TODO serviceRegistry.Register<IPowershellLogger>()\n\n        serviceRegistry.Register<IDizLogger>();\n        serviceRegistry.Register<IProjectFileAssemblyExporter, ProjectFileAssemblyExporter>();\n        serviceRegistry.Register<IProjectFileOpener, ProjectFileReader>();\n    }\n}"
  },
  {
    "path": "Diz.App.PowerShell/DizPowershellLogger.cs",
    "content": "﻿namespace Diz.PowerShell;\n\npublic class DizPowershellLogger : IDizLogger\n{\n    private readonly IPowershellLogger powershellLogger;\n\n    public DizPowershellLogger(IPowershellLogger powershellLogger)\n    {\n        this.powershellLogger = powershellLogger;\n    }\n\n    public void Info(string msg) => \n        powershellLogger.WriteObject(msg);\n\n    public void Warn(string msg) => \n        powershellLogger.WriteCommandDetail(msg);\n\n    public void Error(string msg) =>\n        powershellLogger.WriteObject(msg);\n\n    public void Debug(string msg) => \n        powershellLogger.WriteDebug(msg);\n}"
  },
  {
    "path": "Diz.App.PowerShell/ProjectFileAssemblyExporter.cs",
    "content": "﻿using Diz.Core;\nusing Diz.Core.model;\nusing Diz.Core.util;\nusing Diz.LogWriter;\nusing Diz.LogWriter.util;\n\nnamespace Diz.PowerShell;\n\npublic class ProjectFileAssemblyExporter : IProjectFileAssemblyExporter\n{\n    private readonly IDizLogger logger;\n    private readonly IFilesystemService fs;\n    private readonly IProjectFileOpener projectFileSource;\n\n    public ProjectFileAssemblyExporter(IDizLogger logger, IProjectFileOpener projectFileSource, IFilesystemService fs)\n    {\n        this.logger = logger;\n        this.projectFileSource = projectFileSource;\n        this.fs = fs;\n    }\n    \n    private Project? OpenProjectFile(string projectFileName)\n    {\n        var project = projectFileSource.ReadProjectFromFile(projectFileName);\n        if (project == null)\n            return null;\n\n        logger.Debug($\"Loaded project, rom is: {project.AttachedRomFilename}\");\n        return project;\n    }\n\n    public bool ExportAssembly(string projectFileName)\n    {\n        var project = OpenProjectFile(projectFileName);\n        return project != null && ExportAssembly(project);\n    }\n\n    public bool ExportAssembly(Project project)\n    {\n        var failReason = project.LogWriterSettings.Validate(fs);\n        if (failReason != null)\n        {\n            logger.Error($\"invalid assembly build settings {failReason}\");\n            return false;\n        }\n\n        var lc = new LogCreator\n        {\n            Settings = project.LogWriterSettings,\n            Data = new LogCreatorByteSource(project.Data),\n        };\n\n        logger.Debug(\"Building....\");\n        var result = lc.CreateLog();\n\n        if (!result.Success)\n        {\n            logger.Error($\"Failed to build, error was: {result.AssemblyOutputStr}\");\n            return false;\n        }\n\n        logger.Info(\"Successfully exported assembly output.\");\n        return true;\n    }\n}"
  },
  {
    "path": "Diz.App.PowerShell/ProjectFileReader.cs",
    "content": "﻿using Diz.Core;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\n\nnamespace Diz.PowerShell;\n\npublic class ProjectFileReader : IProjectFileOpener\n{\n    private readonly IProjectFileManager projectFileManager;\n    \n    private string? filename;\n\n    public ProjectFileReader(IProjectFileManager projectFileManager) => \n        this.projectFileManager = projectFileManager;\n\n    public void SetOpenFilename(string projectFilename) => \n        filename = projectFilename;\n\n    public Project? Read()\n    {\n        if (string.IsNullOrEmpty(filename))\n            return null;\n        \n        var openResult = projectFileManager.Open(filename);\n        return openResult?.Root?.Project ?? null;\n    }\n}"
  },
  {
    "path": "Diz.App.PowerShell/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.PowerShell;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizPowerShellCompositionRoot))]"
  },
  {
    "path": "Diz.App.PowerShell/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"DizPowerShell\": {\n      \"commandName\": \"Executable\",\n      \"executablePath\": \"C:\\\\Program Files\\\\PowerShell\\\\7\\\\pwsh.exe\",\n      \"commandLineArgs\": \"-NoProfile -NoExit -Command \\\"../../../runner/debug-startup.ps1\\\"\"\n    }\n  }\n}"
  },
  {
    "path": "Diz.App.PowerShell/PsInterfaces.cs",
    "content": "﻿#nullable enable\n\nnamespace Diz.PowerShell;\n\npublic interface IPowershellLogger\n{\n    void WriteObject(object objectToSend);\n    void WriteDebug(string text);\n    void WriteCommandDetail(string text);\n}\n\npublic interface IDizLogger\n{\n    void Info(string msg);\n    void Warn(string msg);\n    void Error(string s);\n    void Debug(string msg);\n}"
  },
  {
    "path": "Diz.App.PowerShell/ServiceContainerCmdletBase.cs",
    "content": "﻿using System.Management.Automation;\nusing Diz.Core.util;\nusing LightInject;\n\nnamespace Diz.PowerShell;\n\npublic abstract class ServiceContainerCmdletBase : PSCmdlet\n{\n    protected IServiceContainer? ServiceContainer { get; private set; }\n    \n    protected override void BeginProcessing()\n    {\n        ServiceContainer ??= DizServiceProvider.CreateServiceContainer();\n    }\n\n    protected override void EndProcessing()\n    {\n        ServiceContainer?.Dispose();\n        ServiceContainer = null;\n    }\n}"
  },
  {
    "path": "Diz.App.PowerShell/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {\n      \"GitInfo\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.3.3, )\",\n        \"resolved\": \"3.3.3\",\n        \"contentHash\": \"l7m4M/figjWz4B3eIL2Qy3UJiP2vvTOTeavlCT/GGH0khg0uxLanh/cwTZ27AZNhim56q1Mk0nbyRXtLqF4vmg==\",\n        \"dependencies\": {\n          \"ThisAssembly.Constants\": \"1.4.1\"\n        }\n      },\n      \"LightInject\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.6.4, )\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"System.Management.Automation\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[7.4.1, )\",\n        \"resolved\": \"7.4.1\",\n        \"contentHash\": \"EYUMyAoYAw4Zt+cxKRMjZxzoa6gI++O4sK+cSg8HUhC1HfrJoMhD1u1Fo5CvlGv1KX3OmoJSyukgkDmRHFLQiw==\",\n        \"dependencies\": {\n          \"Microsoft.ApplicationInsights\": \"2.21.0\",\n          \"Microsoft.Management.Infrastructure\": \"3.0.0\",\n          \"Microsoft.PowerShell.CoreCLR.Eventing\": \"7.4.1\",\n          \"Microsoft.PowerShell.Native\": \"7.4.0\",\n          \"Microsoft.Security.Extensions\": \"1.2.0\",\n          \"Microsoft.Win32.Registry.AccessControl\": \"8.0.0\",\n          \"Newtonsoft.Json\": \"13.0.3\",\n          \"System.Configuration.ConfigurationManager\": \"8.0.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"8.0.0\",\n          \"System.DirectoryServices\": \"8.0.0\",\n          \"System.Management\": \"8.0.0\",\n          \"System.Security.AccessControl\": \"6.0.0\",\n          \"System.Security.Cryptography.Pkcs\": \"8.0.0\",\n          \"System.Security.Permissions\": \"8.0.0\",\n          \"System.Text.Encoding.CodePages\": \"8.0.0\"\n        }\n      },\n      \"CsvHelper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"30.0.3\",\n        \"contentHash\": \"OK4xEoPKFZYZ2Km+ZTGRxuLo33u+ipKVCC9ICCGn5JRiAmPLRsCPZ/shL3HKGPwS33MQSPcPHigI4HnX3Q47yg==\"\n      },\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"Microsoft.ApplicationInsights\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.21.0\",\n        \"contentHash\": \"btZEDWAFNo9CoYliMCriSMTX3ruRGZTtYw4mo2XyyfLlowFicYVM2Xszi5evDG95QRYV7MbbH3D2RqVwfZlJHw==\",\n        \"dependencies\": {\n          \"System.Diagnostics.DiagnosticSource\": \"5.0.0\"\n        }\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==\"\n      },\n      \"Microsoft.Management.Infrastructure\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"cGZi0q5IujCTVYKo9h22Pw+UwfZDV82HXO8HTxMG2HqntPlT3Ls8jY6punLp4YzCypJNpfCAu2kae3TIyuAiJw==\",\n        \"dependencies\": {\n          \"Microsoft.Management.Infrastructure.Runtime.Unix\": \"3.0.0\",\n          \"Microsoft.Management.Infrastructure.Runtime.Win\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Management.Infrastructure.Runtime.Unix\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"QZE3uEDvZ0m7LabQvcmNOYHp7v1QPBVMpB/ild0WEE8zqUVAP5y9rRI5we37ImI1bQmW5pZ+3HNC70POPm0jBQ==\"\n      },\n      \"Microsoft.Management.Infrastructure.Runtime.Win\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"uwMyWN33+iQ8Wm/n1yoPXgFoiYNd0HzJyoqSVhaQZyJfaQrJR3udgcIHjqa1qbc3lS6kvfuUMN4TrF4U4refCQ==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.PowerShell.CoreCLR.Eventing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.4.1\",\n        \"contentHash\": \"uyByMNZ3XVUrJAxdHrXM/75vcKdfbs04J5iIZfDA8m9z8TJDViRMjyHNcp8K/ZXyzpT2Lua2d7g+dP47E9wAcg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.EventLog\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.PowerShell.Native\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.4.0\",\n        \"contentHash\": \"FlaJ3JBWhqFToYT0ycMb/Xxzoof7oTQbNyI4UikgubC7AMWt5ptBNKjIAMPvOcvEHr+ohaO9GvRWp3tiyS3sKw==\"\n      },\n      \"Microsoft.Security.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.2.0\",\n        \"contentHash\": \"GjHZBE5PHKrxPRyGujWQKwbKNjPQYds6HcAWKeV49X3KPgBfF2B1vV5uJey5UluyGQlvAO/DezL7WzEx9HlPQA==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.Registry.AccessControl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"u8PB9/v02C8mBXzl0vJ7bOyC020zOP+T1mRct+KA46DqZkB40XtsNn9pGD0QowTRsT6R4jPCghn+yAODn2UMMw==\"\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"13.0.3\",\n        \"contentHash\": \"HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==\"\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.CodeDom\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"WTlRjL6KWIMr/pAaq3rYqh0TJlzpouaQ/W1eelssHgtlwHAH25jXTkUphTYx9HaIIf7XA6qs/0+YhtLEQRkJ+Q==\"\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Configuration.ConfigurationManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==\",\n        \"dependencies\": {\n          \"System.Diagnostics.EventLog\": \"8.0.0\",\n          \"System.Security.Cryptography.ProtectedData\": \"8.0.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==\"\n      },\n      \"System.Diagnostics.EventLog\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==\"\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.DirectoryServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"7nit//efUTy1OsAKco2f02PMrwsR2S234N0dVVp84udC77YcvpOQDz5znAWMtgMWBzY1aRJvUW61jo/7vQRfXg==\"\n      },\n      \"System.Formats.Asn1\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"AJukBuLoe3QeAF+mfaRKQb2dgyrvt340iMBHYv+VdBzCUM06IxGlvl0o/uPOS7lHnXPN6u8fFRHSHudx5aTi8w==\"\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Management\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==\",\n        \"dependencies\": {\n          \"System.CodeDom\": \"8.0.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.AccessControl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==\"\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Pkcs\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"ULmp3xoOwNYjOYp4JZ2NK/6NdTgiN1GQXzVVN1njQ7LOZ0d0B9vyMnhyqbIi9Qw4JXj1JgCsitkTShboHRx7Eg==\",\n        \"dependencies\": {\n          \"System.Formats.Asn1\": \"8.0.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.ProtectedData\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==\"\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Permissions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"v/BBylw7XevuAsHXoX9dDUUfmBIcUf7Lkz8K3ZXIKz3YRKpw8YftpSir4n4e/jDTKFoaK37AsC3xnk+GNFI1Ow==\",\n        \"dependencies\": {\n          \"System.Windows.Extensions\": \"8.0.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.4\",\n        \"contentHash\": \"zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==\"\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Windows.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"Obg3a90MkOw9mYKxrardLpY2u0axDMrSmy4JCdq2cYbelM2cUwmUir5Bomvd1yxmPL9h5LVHU1tuKBZpUjfASg==\"\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"ThisAssembly.Constants\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.1\",\n        \"contentHash\": \"sV0CDYlC6YSIkYbdwd4+jRinLb888Dac71pmNhF2Ll6UXY1J+1oVt3EfmX6euXEKz2RLdfV+4GyZctfEA0NSqA==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.7.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.4\"\n        }\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"FluentValidation\": \"[11.9.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"JetBrains.Profiler.SelfApi\": \"[2.5.0, )\",\n          \"LightInject\": \"[6.6.4, )\",\n          \"SharpZipLib\": \"[1.4.2, )\",\n          \"System.Diagnostics.Tracing\": \"[4.3.0, )\",\n          \"System.Resources.Extensions\": \"[8.0.0, )\",\n          \"System.Text.Encoding.CodePages\": \"[8.0.0, )\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      },\n      \"diz.cpu.65816\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.logwriter\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CsvHelper\": \"[30.0.3, )\",\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.App.PowerShell/runner/debug-startup.ps1",
    "content": "﻿$module = \"Diz.PowerShell\"\n$basepath = \".\\\"\n\necho \"Starting...\"\nImport-Module \"$($basepath)$($module).dll\"\n\nWRite-Host \"Current working dir---> $($pwd)'\"\n\nif ((Get-Command -module $module).Count -eq 0) { \n  Write-Host \"WARNING: Couldn't find our module. (build issue?)\" -ForegroundColor red\n} else {\n  Write-Host \"Ready!\" -ForegroundColor green\n}\n\n$dizproject = \"testproject.dizraw\"\n$cmd_to_run = \"Build-AssemblyFiles -ProjectNames (Resolve-Path '..\\..\\..\\..\\..\\rom\\$($dizproject)')\"\n\nWrite-Host \"Press enter to run this command: '$($cmd_to_run)'\"\n$null = Read-Host\n\nInvoke-Expression $cmd_to_run"
  },
  {
    "path": "Diz.App.Winforms/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <runtime>\n        <!-- \n        allow loading of assemblies from a different directory. important for final release if copying here\n        if messing with this, also check out DiztinGUIsh.runtimeconfig.json in the publish/ folder. mess with\n        \"additionalProbingPaths\".  it all doesn't quite work anymore in .NET5\n        \n        2025 update: we don't really rely on this and just copy everything into one flat directory.  \n        -->\n        \n        <!-- <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\"> -->\n        <!--     <probing privatePath=\"lib\" /> -->\n        <!-- </assemblyBinding> -->\n    </runtime>\n</configuration>"
  },
  {
    "path": "Diz.App.Winforms/AppVersionInfo.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing Diz.Core.Interfaces;\nusing Diz.Core.serialization.xml_serializer;\n\nnamespace Diz.App.Winforms;\n\n[SuppressMessage(\"ReSharper\", \"ClassNeverInstantiated.Global\")]\npublic class AppVersionInfo : IAppVersionInfo\n{\n    private readonly string versionStr;\n    private readonly string fullBuildDescription;\n    \n    public AppVersionInfo()\n    {\n        // cache these, they won't change\n        versionStr = ThisAssembly.Git.Tag;\n        fullBuildDescription =\n            $\"Build info:\\r\\n------------\\r\\n\" +\n            $\"Version: {versionStr}\\r\\n\" +\n            $\"Git branch: {ThisAssembly.Git.Branch}\\r\\n\" +\n            $\"Git commit: {ThisAssembly.Git.Commit}\\r\\n\" +\n            $\"Git repo URL: {ThisAssembly.Git.RepositoryUrl}\\r\\n\" +\n            $\"Git tag: {ThisAssembly.Git.Tag}\\r\\n\" +\n            $\"Git last commit date: {ThisAssembly.Git.CommitDate}\\r\\n\" +\n            $\"Git IsDirty: {ThisAssembly.Git.IsDirtyString}\\r\\n\" +\n            $\"Git Commits on top of base: {ThisAssembly.Git.Commits}\\r\\n\" +\n            \"\\r\\n\\r\\n\" +\n            $\"Diz app savefile format ver: {ProjectXmlSerializer.LatestSaveFormatVersion}\";\n    }\n    \n    [SuppressMessage(\"ReSharper\", \"HeuristicUnreachableCode\")]\n    public string GetVersionInfo(IAppVersionInfo.AppVersionInfoType type)\n    {\n        return type switch\n        {\n            IAppVersionInfo.AppVersionInfoType.FullDescription => fullBuildDescription,\n            IAppVersionInfo.AppVersionInfoType.Version => versionStr,\n            _ => \"\"\n        };\n    }\n}"
  },
  {
    "path": "Diz.App.Winforms/Diz.App.Winforms.csproj",
    "content": "﻿<Project>\n  <Import Project=\"Sdk.props\" Sdk=\"Microsoft.NET.Sdk\" />\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net9.0-windows</TargetFramework>\n    <UseWindowsForms>true</UseWindowsForms>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <RootNamespace>Diz.App.Winforms</RootNamespace>\n    <PackageId>Diz.App.Winforms</PackageId>\n    <ApplicationIcon>resource/diz-icon2-magenta-med32x32.ico</ApplicationIcon>\n    <LangVersion>latest</LangVersion>\n    <GitThisAssemblyMetadata>true</GitThisAssemblyMetadata>\n    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>\n    \n    <!-- for self-contained publishing -->\n    <RuntimeIdentifiers>win-x64;win-x86</RuntimeIdentifiers>\n  </PropertyGroup>\n  \n  <Import Project=\"Sdk.targets\" Sdk=\"Microsoft.NET.Sdk\" />  <!-- import this so our custom targets work -->\n  \n  <ItemGroup>\n    <ProjectReference Include=\"..\\Diz.App.Common\\Diz.App.Common.csproj\" />\n    <ProjectReference Include=\"..\\Diz.Controllers\\Diz.Controllers\\Diz.Controllers.csproj\" />\n    <ProjectReference Include=\"..\\Diz.Ui.Winforms\\Diz.Ui.Winforms\\Diz.Ui.Winforms.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"JetBrains.Annotations\">\n      <Version>2023.3.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"GitInfo\">\n      <Version>3.3.3</Version>\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"LightInject\">\n      <Version>6.6.4</Version>\n    </PackageReference>\n    <PackageReference Include=\"System.Resources.Extensions\" Version=\"8.0.0\" />\n  </ItemGroup>\n\n\n  <ItemGroup>\n    <EmbeddedResource Update=\"window\\usercontrols\\visualizer\\legend\\BankLegend.resx\">\n      <DependentUpon>BankLegend.cs</DependentUpon>\n    </EmbeddedResource>\n    <EmbeddedResource Update=\"window\\usercontrols\\visualizer\\legend\\BankLegendItem.resx\">\n      <DependentUpon>BankLegendItem.cs</DependentUpon>\n    </EmbeddedResource>\n    <EmbeddedResource Update=\"window\\usercontrols\\visualizer\\graphics\\RomBankVisualizer.resx\">\n      <DependentUpon>RomBankVisualizer.cs</DependentUpon>\n    </EmbeddedResource>\n    <EmbeddedResource Update=\"window\\usercontrols\\visualizer\\graphics\\RomFullVisualizer.resx\">\n      <DependentUpon>RomFullVisualizer.cs</DependentUpon>\n    </EmbeddedResource>\n    <EmbeddedResource Update=\"window\\usercontrols\\visualizer\\graphics\\RomImage.resx\">\n      <DependentUpon>RomImage.cs</DependentUpon>\n    </EmbeddedResource>\n    <EmbeddedResource Update=\"window\\DataGridEditorForm.resx\">\n      <DependentUpon>DataGridEditorForm.cs</DependentUpon>\n      <SubType>Designer</SubType>\n    </EmbeddedResource>\n    <None Include=\"build\\TidyPublishDir.targets\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"..\\.github\\workflows\\dotnet.yml\">\n      <Link>diztinguish\\.github\\workflows\\dotnet.yml</Link>\n    </Content>\n  </ItemGroup>\n  <!--custom build targets for packing up final distribution of files-->\n  <Import Project=\"build/TidyPublishDir.targets\" />\n  \n  \n</Project>"
  },
  {
    "path": "Diz.App.Winforms/Diz.App.Winforms.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:String x:Key=\"/Default/CodeEditing/Localization/Localizable/@EntryValue\">No</s:String></wpf:ResourceDictionary>"
  },
  {
    "path": "Diz.App.Winforms/DizAppWinformsCompositionRoot.cs",
    "content": "﻿using Diz.App.Common;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.Interfaces;\nusing Diz.Ui.Winforms;\nusing Diz.Ui.Winforms.dialogs;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.App.Winforms;\n\n[UsedImplicitly] public class DizAppWinformsCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.RegisterFrom<DizAppCommonCompositionRoot>();\n        \n        serviceRegistry.Register<IDizApp, DizWinformsApp>();\n        serviceRegistry.Register<ICommonGui, WinFormsCommonGui>();\n        serviceRegistry.Register<IAppVersionInfo, AppVersionInfo>();\n    }\n}"
  },
  {
    "path": "Diz.App.Winforms/Program.cs",
    "content": "﻿#nullable enable\n\n// #define DEBUG_EXTRA_CRASH_HANDLING // for catching really stubborn crashes, like in databinding\n\nusing System;\n#if DEBUG_EXTRA_CRASH_HANDLING\nusing System.IO;\nusing System.Windows.Forms;\n#endif\nusing Diz.App.Common;\n\nnamespace Diz.App.Winforms;\n\ninternal static class Program\n{\n    [STAThread]\n    private static void Main(string[] args)\n    {\n        #if DEBUG_EXTRA_CRASH_HANDLING\n        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); // dangerous\n        Application.ThreadException += (sender, e) => \n        {\n            File.WriteAllText(\"crash_log.txt\", $\"Thread Exception: {e.Exception}\");\n            MessageBox.Show($\"Thread Exception: {e.Exception}\");\n        };\n    \n        AppDomain.CurrentDomain.UnhandledException += (sender, e) => \n        {\n            File.WriteAllText(\"crash_log.txt\", $\"Unhandled Exception: {e.ExceptionObject}\");\n            MessageBox.Show($\"Unhandled Exception: {e.ExceptionObject}\");\n        };\n        #endif\n        \n        var serviceFactory = DizWinformsRegisterServices.CreateServiceFactoryAndRegisterTypes();\n        DizAppCommon.StartApp(serviceFactory, args);\n    }\n}"
  },
  {
    "path": "Diz.App.Winforms/Properties/Annotations.cs",
    "content": "﻿// Note from Dom/Diztinguish: Jetbrains added this. Not sure we really need it,\n// remove if you feel like it. probably won't hurt anything, just editor functionality.\n\n/* MIT License\n\nCopyright (c) 2016 JetBrains http://www.jetbrains.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE. */\n\nusing System;\n\n// ReSharper disable InheritdocConsiderUsage\n\n#pragma warning disable 1591\n// ReSharper disable UnusedMember.Global\n// ReSharper disable MemberCanBePrivate.Global\n// ReSharper disable UnusedAutoPropertyAccessor.Global\n// ReSharper disable IntroduceOptionalParameters.Global\n// ReSharper disable MemberCanBeProtected.Global\n// ReSharper disable InconsistentNaming\n\nnamespace Diz.App.Winforms.Properties\n{\n  /// <summary>\n  /// Indicates that the value of the marked element could be <c>null</c> sometimes,\n  /// so checking for <c>null</c> is required before its usage.\n  /// </summary>\n  /// <example><code>\n  /// [CanBeNull] object Test() => null;\n  /// \n  /// void UseTest() {\n  ///   var p = Test();\n  ///   var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |\n    AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event |\n    AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)]\n  public sealed class CanBeNullAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the value of the marked element can never be <c>null</c>.\n  /// </summary>\n  /// <example><code>\n  /// [NotNull] object Foo() {\n  ///   return null; // Warning: Possible 'null' assignment\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |\n    AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event |\n    AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)]\n  public sealed class NotNullAttribute : Attribute { }\n\n  /// <summary>\n  /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task\n  /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property\n  /// or of the Lazy.Value property can never be null.\n  /// </summary>\n  /// <example><code>\n  /// public void Foo([ItemNotNull]List&lt;string&gt; books)\n  /// {\n  ///   foreach (var book in books) {\n  ///     if (book != null) // Warning: Expression is always true\n  ///      Console.WriteLine(book.ToUpper());\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |\n    AttributeTargets.Delegate | AttributeTargets.Field)]\n  public sealed class ItemNotNullAttribute : Attribute { }\n\n  /// <summary>\n  /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task\n  /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property\n  /// or of the Lazy.Value property can be null.\n  /// </summary>\n  /// <example><code>\n  /// public void Foo([ItemCanBeNull]List&lt;string&gt; books)\n  /// {\n  ///   foreach (var book in books)\n  ///   {\n  ///     // Warning: Possible 'System.NullReferenceException'\n  ///     Console.WriteLine(book.ToUpper());\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |\n    AttributeTargets.Delegate | AttributeTargets.Field)]\n  public sealed class ItemCanBeNullAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the marked method builds string by the format pattern and (optional) arguments.\n  /// The parameter, which contains the format string, should be given in constructor. The format string\n  /// should be in <see cref=\"string.Format(IFormatProvider,string,object[])\"/>-like form.\n  /// </summary>\n  /// <example><code>\n  /// [StringFormatMethod(\"message\")]\n  /// void ShowError(string message, params object[] args) { /* do something */ }\n  /// \n  /// void Foo() {\n  ///   ShowError(\"Failed: {0}\"); // Warning: Non-existing argument in format string\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Constructor | AttributeTargets.Method |\n    AttributeTargets.Property | AttributeTargets.Delegate)]\n  public sealed class StringFormatMethodAttribute : Attribute\n  {\n    /// <param name=\"formatParameterName\">\n    /// Specifies which parameter of an annotated method should be treated as the format string\n    /// </param>\n    public StringFormatMethodAttribute([NotNull] string formatParameterName)\n    {\n      FormatParameterName = formatParameterName;\n    }\n\n    [NotNull] public string FormatParameterName { get; }\n  }\n\n  /// <summary>\n  /// Use this annotation to specify a type that contains static or const fields\n  /// with values for the annotated property/field/parameter.\n  /// The specified type will be used to improve completion suggestions.\n  /// </summary>\n  /// <example><code>\n  /// namespace TestNamespace\n  /// {\n  ///   public class Constants\n  ///   {\n  ///     public static int INT_CONST = 1;\n  ///     public const string STRING_CONST = \"1\";\n  ///   }\n  ///\n  ///   public class Class1\n  ///   {\n  ///     [ValueProvider(\"TestNamespace.Constants\")] public int myField;\n  ///     public void Foo([ValueProvider(\"TestNamespace.Constants\")] string str) { }\n  ///\n  ///     public void Test()\n  ///     {\n  ///       Foo(/*try completion here*/);//\n  ///       myField = /*try completion here*/\n  ///     }\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field,\n    AllowMultiple = true)]\n  public sealed class ValueProviderAttribute : Attribute\n  {\n    public ValueProviderAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; }\n  }\n\n  /// <summary>\n  /// Indicates that the integral value falls into the specified interval.\n  /// It's allowed to specify multiple non-intersecting intervals.\n  /// Values of interval boundaries are inclusive.\n  /// </summary>\n  /// <example><code>\n  /// void Foo([ValueRange(0, 100)] int value) {\n  ///   if (value == -1) { // Warning: Expression is always 'false'\n  ///     ...\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property |\n    AttributeTargets.Method | AttributeTargets.Delegate,\n    AllowMultiple = true)]\n  public sealed class ValueRangeAttribute : Attribute\n  {\n    public object From { get; }\n    public object To { get; }\n\n    public ValueRangeAttribute(long from, long to)\n    {\n      From = from;\n      To = to;\n    }\n\n    public ValueRangeAttribute(ulong from, ulong to)\n    {\n      From = from;\n      To = to;\n    }\n\n    public ValueRangeAttribute(long value)\n    {\n      From = To = value;\n    }\n\n    public ValueRangeAttribute(ulong value)\n    {\n      From = To = value;\n    }\n  }\n\n  /// <summary>\n  /// Indicates that the integral value never falls below zero.\n  /// </summary>\n  /// <example><code>\n  /// void Foo([NonNegativeValue] int value) {\n  ///   if (value == -1) { // Warning: Expression is always 'false'\n  ///     ...\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property |\n    AttributeTargets.Method | AttributeTargets.Delegate)]\n  public sealed class NonNegativeValueAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the function argument should be a string literal and match one\n  /// of the parameters of the caller function. For example, ReSharper annotates\n  /// the parameter of <see cref=\"System.ArgumentNullException\"/>.\n  /// </summary>\n  /// <example><code>\n  /// void Foo(string param) {\n  ///   if (param == null)\n  ///     throw new ArgumentNullException(\"par\"); // Warning: Cannot resolve symbol\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class InvokerParameterNameAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the method is contained in a type that implements\n  /// <c>System.ComponentModel.INotifyPropertyChanged</c> interface and this method\n  /// is used to notify that some property value changed.\n  /// </summary>\n  /// <remarks>\n  /// The method should be non-static and conform to one of the supported signatures:\n  /// <list>\n  /// <item><c>NotifyChanged(string)</c></item>\n  /// <item><c>NotifyChanged(params string[])</c></item>\n  /// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>\n  /// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>\n  /// <item><c>SetProperty{T}(ref T, T, string)</c></item>\n  /// </list>\n  /// </remarks>\n  /// <example><code>\n  /// public class Foo : INotifyPropertyChanged {\n  ///   public event PropertyChangedEventHandler PropertyChanged;\n  /// \n  ///   [NotifyPropertyChangedInvocator]\n  ///   protected virtual void NotifyChanged(string propertyName) { ... }\n  ///\n  ///   string _name;\n  /// \n  ///   public string Name {\n  ///     get { return _name; }\n  ///     set { _name = value; NotifyChanged(\"LastName\"); /* Warning */ }\n  ///   }\n  /// }\n  /// </code>\n  /// Examples of generated notifications:\n  /// <list>\n  /// <item><c>NotifyChanged(\"Property\")</c></item>\n  /// <item><c>NotifyChanged(() =&gt; Property)</c></item>\n  /// <item><c>NotifyChanged((VM x) =&gt; x.Property)</c></item>\n  /// <item><c>SetProperty(ref myField, value, \"Property\")</c></item>\n  /// </list>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute\n  {\n    public NotifyPropertyChangedInvocatorAttribute() { }\n    public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName)\n    {\n      ParameterName = parameterName;\n    }\n\n    [CanBeNull] public string ParameterName { get; }\n  }\n\n  /// <summary>\n  /// Describes dependency between method input and output.\n  /// </summary>\n  /// <syntax>\n  /// <p>Function Definition Table syntax:</p>\n  /// <list>\n  /// <item>FDT      ::= FDTRow [;FDTRow]*</item>\n  /// <item>FDTRow   ::= Input =&gt; Output | Output &lt;= Input</item>\n  /// <item>Input    ::= ParameterName: Value [, Input]*</item>\n  /// <item>Output   ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>\n  /// <item>Value    ::= true | false | null | notnull | canbenull</item>\n  /// </list>\n  /// If the method has a single input parameter, its name could be omitted.<br/>\n  /// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same) for the method output\n  /// means that the method doesn't return normally (throws or terminates the process).<br/>\n  /// Value <c>canbenull</c> is only applicable for output parameters.<br/>\n  /// You can use multiple <c>[ContractAnnotation]</c> for each FDT row, or use single attribute\n  /// with rows separated by semicolon. There is no notion of order rows, all rows are checked\n  /// for applicability and applied per each program state tracked by the analysis engine.<br/>\n  /// </syntax>\n  /// <examples><list>\n  /// <item><code>\n  /// [ContractAnnotation(\"=&gt; halt\")]\n  /// public void TerminationMethod()\n  /// </code></item>\n  /// <item><code>\n  /// [ContractAnnotation(\"null &lt;= param:null\")] // reverse condition syntax\n  /// public string GetName(string surname)\n  /// </code></item>\n  /// <item><code>\n  /// [ContractAnnotation(\"s:null =&gt; true\")]\n  /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()\n  /// </code></item>\n  /// <item><code>\n  /// // A method that returns null if the parameter is null,\n  /// // and not null if the parameter is not null\n  /// [ContractAnnotation(\"null =&gt; null; notnull =&gt; notnull\")]\n  /// public object Transform(object data)\n  /// </code></item>\n  /// <item><code>\n  /// [ContractAnnotation(\"=&gt; true, result: notnull; =&gt; false, result: null\")]\n  /// public bool TryParse(string s, out Person result)\n  /// </code></item>\n  /// </list></examples>\n  [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]\n  public sealed class ContractAnnotationAttribute : Attribute\n  {\n    public ContractAnnotationAttribute([NotNull] string contract)\n      : this(contract, false) { }\n\n    public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)\n    {\n      Contract = contract;\n      ForceFullStates = forceFullStates;\n    }\n\n    [NotNull] public string Contract { get; }\n\n    public bool ForceFullStates { get; }\n  }\n\n  /// <summary>\n  /// Indicates whether the marked element should be localized.\n  /// </summary>\n  /// <example><code>\n  /// [LocalizationRequiredAttribute(true)]\n  /// class Foo {\n  ///   string str = \"my string\"; // Warning: Localizable string\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.All)]\n  public sealed class LocalizationRequiredAttribute : Attribute\n  {\n    public LocalizationRequiredAttribute() : this(true) { }\n\n    public LocalizationRequiredAttribute(bool required)\n    {\n      Required = required;\n    }\n\n    public bool Required { get; }\n  }\n\n  /// <summary>\n  /// Indicates that the value of the marked type (or its derivatives)\n  /// cannot be compared using '==' or '!=' operators and <c>Equals()</c>\n  /// should be used instead. However, using '==' or '!=' for comparison\n  /// with <c>null</c> is always permitted.\n  /// </summary>\n  /// <example><code>\n  /// [CannotApplyEqualityOperator]\n  /// class NoEquality { }\n  /// \n  /// class UsesNoEquality {\n  ///   void Test() {\n  ///     var ca1 = new NoEquality();\n  ///     var ca2 = new NoEquality();\n  ///     if (ca1 != null) { // OK\n  ///       bool condition = ca1 == ca2; // Warning\n  ///     }\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]\n  public sealed class CannotApplyEqualityOperatorAttribute : Attribute { }\n\n  /// <summary>\n  /// When applied to a target attribute, specifies a requirement for any type marked\n  /// with the target attribute to implement or inherit specific type or types.\n  /// </summary>\n  /// <example><code>\n  /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement\n  /// class ComponentAttribute : Attribute { }\n  /// \n  /// [Component] // ComponentAttribute requires implementing IComponent interface\n  /// class MyComponent : IComponent { }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n  [BaseTypeRequired(typeof(Attribute))]\n  public sealed class BaseTypeRequiredAttribute : Attribute\n  {\n    public BaseTypeRequiredAttribute([NotNull] Type baseType)\n    {\n      BaseType = baseType;\n    }\n\n    [NotNull] public Type BaseType { get; }\n  }\n\n  /// <summary>\n  /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library),\n  /// so this symbol will not be reported as unused (as well as by other usage inspections).\n  /// </summary>\n  [AttributeUsage(AttributeTargets.All)]\n  public sealed class UsedImplicitlyAttribute : Attribute\n  {\n    public UsedImplicitlyAttribute()\n      : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)\n      : this(useKindFlags, ImplicitUseTargetFlags.Default) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)\n      : this(ImplicitUseKindFlags.Default, targetFlags) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)\n    {\n      UseKindFlags = useKindFlags;\n      TargetFlags = targetFlags;\n    }\n\n    public ImplicitUseKindFlags UseKindFlags { get; }\n\n    public ImplicitUseTargetFlags TargetFlags { get; }\n  }\n\n  /// <summary>\n  /// Can be applied to attributes, type parameters, and parameters of a type assignable from <see cref=\"System.Type\"/> .\n  /// When applied to an attribute, the decorated attribute behaves the same as <see cref=\"UsedImplicitlyAttribute\"/>.\n  /// When applied to a type parameter or to a parameter of type <see cref=\"System.Type\"/>,  indicates that the corresponding type\n  /// is used implicitly.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)]\n  public sealed class MeansImplicitUseAttribute : Attribute\n  {\n    public MeansImplicitUseAttribute()\n      : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)\n      : this(useKindFlags, ImplicitUseTargetFlags.Default) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)\n      : this(ImplicitUseKindFlags.Default, targetFlags) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)\n    {\n      UseKindFlags = useKindFlags;\n      TargetFlags = targetFlags;\n    }\n\n    [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; }\n\n    [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; }\n  }\n\n  /// <summary>\n  /// Specify the details of implicitly used symbol when it is marked\n  /// with <see cref=\"MeansImplicitUseAttribute\"/> or <see cref=\"UsedImplicitlyAttribute\"/>.\n  /// </summary>\n  [Flags]\n  public enum ImplicitUseKindFlags\n  {\n    Default = Access | Assign | InstantiatedWithFixedConstructorSignature,\n    /// <summary>Only entity marked with attribute considered used.</summary>\n    Access = 1,\n    /// <summary>Indicates implicit assignment to a member.</summary>\n    Assign = 2,\n    /// <summary>\n    /// Indicates implicit instantiation of a type with fixed constructor signature.\n    /// That means any unused constructor parameters won't be reported as such.\n    /// </summary>\n    InstantiatedWithFixedConstructorSignature = 4,\n    /// <summary>Indicates implicit instantiation of a type.</summary>\n    InstantiatedNoFixedConstructorSignature = 8,\n  }\n\n  /// <summary>\n  /// Specify what is considered to be used implicitly when marked\n  /// with <see cref=\"MeansImplicitUseAttribute\"/> or <see cref=\"UsedImplicitlyAttribute\"/>.\n  /// </summary>\n  [Flags]\n  public enum ImplicitUseTargetFlags\n  {\n    Default = Itself,\n    Itself = 1,\n    /// <summary>Members of entity marked with attribute are considered used.</summary>\n    Members = 2,\n    /// <summary> Inherited entities are considered used. </summary>\n    WithInheritors = 4,\n    /// <summary>Entity marked with attribute and all its members considered used.</summary>\n    WithMembers = Itself | Members\n  }\n\n  /// <summary>\n  /// This attribute is intended to mark publicly available API\n  /// which should not be removed and so is treated as used.\n  /// </summary>\n  [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]\n  [AttributeUsage(AttributeTargets.All, Inherited = false)]\n  public sealed class PublicAPIAttribute : Attribute\n  {\n    public PublicAPIAttribute() { }\n\n    public PublicAPIAttribute([NotNull] string comment)\n    {\n      Comment = comment;\n    }\n\n    [CanBeNull] public string Comment { get; }\n  }\n\n  /// <summary>\n  /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack.\n  /// If the parameter is a delegate, indicates that delegate is executed while the method is executed.\n  /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class InstantHandleAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that a method does not make any observable state changes.\n  /// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>.\n  /// </summary>\n  /// <example><code>\n  /// [Pure] int Multiply(int x, int y) => x * y;\n  /// \n  /// void M() {\n  ///   Multiply(123, 42); // Warning: Return value of pure method is not used\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class PureAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the return value of the method invocation must be used.\n  /// </summary>\n  /// <remarks>\n  /// Methods decorated with this attribute (in contrast to pure methods) might change state,\n  /// but make no sense without using their return value. <br/>\n  /// Similarly to <see cref=\"PureAttribute\"/>, this attribute\n  /// will help detecting usages of the method when the return value in not used.\n  /// Additionally, you can optionally specify a custom message, which will be used when showing warnings, e.g.\n  /// <code>[MustUseReturnValue(\"Use the return value to...\")]</code>.\n  /// </remarks>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class MustUseReturnValueAttribute : Attribute\n  {\n    public MustUseReturnValueAttribute() { }\n\n    public MustUseReturnValueAttribute([NotNull] string justification)\n    {\n      Justification = justification;\n    }\n\n    [CanBeNull] public string Justification { get; }\n  }\n\n  /// <summary>\n  /// Indicates the type member or parameter of some type, that should be used instead of all other ways\n  /// to get the value of that type. This annotation is useful when you have some \"context\" value evaluated\n  /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one.\n  /// </summary>\n  /// <example><code>\n  /// class Foo {\n  ///   [ProvidesContext] IBarService _barService = ...;\n  /// \n  ///   void ProcessNode(INode node) {\n  ///     DoSomething(node, node.GetGlobalServices().Bar);\n  ///     //              ^ Warning: use value of '_barService' field\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(\n    AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method |\n    AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)]\n  public sealed class ProvidesContextAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that a parameter is a path to a file or a folder within a web project.\n  /// Path can be relative or absolute, starting from web root (~).\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class PathReferenceAttribute : Attribute\n  {\n    public PathReferenceAttribute() { }\n\n    public PathReferenceAttribute([NotNull, PathReference] string basePath)\n    {\n      BasePath = basePath;\n    }\n\n    [CanBeNull] public string BasePath { get; }\n  }\n\n  /// <summary>\n  /// An extension method marked with this attribute is processed by code completion\n  /// as a 'Source Template'. When the extension method is completed over some expression, its source code\n  /// is automatically expanded like a template at call site.\n  /// </summary>\n  /// <remarks>\n  /// Template method body can contain valid source code and/or special comments starting with '$'.\n  /// Text inside these comments is added as source code when the template is applied. Template parameters\n  /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs.\n  /// Use the <see cref=\"MacroAttribute\"/> attribute to specify macros for parameters.\n  /// </remarks>\n  /// <example>\n  /// In this example, the 'forEach' method is a source template available over all values\n  /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block:\n  /// <code>\n  /// [SourceTemplate]\n  /// public static void forEach&lt;T&gt;(this IEnumerable&lt;T&gt; xs) {\n  ///   foreach (var x in xs) {\n  ///      //$ $END$\n  ///   }\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class SourceTemplateAttribute : Attribute { }\n\n  /// <summary>\n  /// Allows specifying a macro for a parameter of a <see cref=\"SourceTemplateAttribute\">source template</see>.\n  /// </summary>\n  /// <remarks>\n  /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression\n  /// is defined in the <see cref=\"MacroAttribute.Expression\"/> property. When applied on a method, the target\n  /// template parameter is defined in the <see cref=\"MacroAttribute.Target\"/> property. To apply the macro silently\n  /// for the parameter, set the <see cref=\"MacroAttribute.Editable\"/> property value = -1.\n  /// </remarks>\n  /// <example>\n  /// Applying the attribute on a source template method:\n  /// <code>\n  /// [SourceTemplate, Macro(Target = \"item\", Expression = \"suggestVariableName()\")]\n  /// public static void forEach&lt;T&gt;(this IEnumerable&lt;T&gt; collection) {\n  ///   foreach (var item in collection) {\n  ///     //$ $END$\n  ///   }\n  /// }\n  /// </code>\n  /// Applying the attribute on a template method parameter:\n  /// <code>\n  /// [SourceTemplate]\n  /// public static void something(this Entity x, [Macro(Expression = \"guid()\", Editable = -1)] string newguid) {\n  ///   /*$ var $x$Id = \"$newguid$\" + x.ToString();\n  ///   x.DoSomething($x$Id); */\n  /// }\n  /// </code>\n  /// </example>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)]\n  public sealed class MacroAttribute : Attribute\n  {\n    /// <summary>\n    /// Allows specifying a macro that will be executed for a <see cref=\"SourceTemplateAttribute\">source template</see>\n    /// parameter when the template is expanded.\n    /// </summary>\n    [CanBeNull] public string Expression { get; set; }\n\n    /// <summary>\n    /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed.\n    /// </summary>\n    /// <remarks>\n    /// If the target parameter is used several times in the template, only one occurrence becomes editable;\n    /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence,\n    /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1.\n    /// </remarks>\n    public int Editable { get; set; }\n\n    /// <summary>\n    /// Identifies the target parameter of a <see cref=\"SourceTemplateAttribute\">source template</see> if the\n    /// <see cref=\"MacroAttribute\"/> is applied on a template method.\n    /// </summary>\n    [CanBeNull] public string Target { get; set; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]\n  public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute\n  {\n    public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format)\n    {\n      Format = format;\n    }\n\n    [NotNull] public string Format { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]\n  public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format)\n    {\n      Format = format;\n    }\n\n    [NotNull] public string Format { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]\n  public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcAreaViewLocationFormatAttribute([NotNull] string format)\n    {\n      Format = format;\n    }\n\n    [NotNull] public string Format { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]\n  public sealed class AspMvcMasterLocationFormatAttribute : Attribute\n  {\n    public AspMvcMasterLocationFormatAttribute([NotNull] string format)\n    {\n      Format = format;\n    }\n\n    [NotNull] public string Format { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]\n  public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcPartialViewLocationFormatAttribute([NotNull] string format)\n    {\n      Format = format;\n    }\n\n    [NotNull] public string Format { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]\n  public sealed class AspMvcViewLocationFormatAttribute : Attribute\n  {\n    public AspMvcViewLocationFormatAttribute([NotNull] string format)\n    {\n      Format = format;\n    }\n\n    [NotNull] public string Format { get; }\n  }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter\n  /// is an MVC action. If applied to a method, the MVC action name is calculated\n  /// implicitly from the context. Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcActionAttribute : Attribute\n  {\n    public AspMvcActionAttribute() { }\n\n    public AspMvcActionAttribute([NotNull] string anonymousProperty)\n    {\n      AnonymousProperty = anonymousProperty;\n    }\n\n    [CanBeNull] public string AnonymousProperty { get; }\n  }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area.\n  /// Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcAreaAttribute : Attribute\n  {\n    public AspMvcAreaAttribute() { }\n\n    public AspMvcAreaAttribute([NotNull] string anonymousProperty)\n    {\n      AnonymousProperty = anonymousProperty;\n    }\n\n    [CanBeNull] public string AnonymousProperty { get; }\n  }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is\n  /// an MVC controller. If applied to a method, the MVC controller name is calculated\n  /// implicitly from the context. Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcControllerAttribute : Attribute\n  {\n    public AspMvcControllerAttribute() { }\n\n    public AspMvcControllerAttribute([NotNull] string anonymousProperty)\n    {\n      AnonymousProperty = anonymousProperty;\n    }\n\n    [CanBeNull] public string AnonymousProperty { get; }\n  }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute\n  /// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcMasterAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute\n  /// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, Object)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AspMvcModelTypeAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC\n  /// partial view. If applied to a method, the MVC partial view name is calculated implicitly\n  /// from the context. Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcPartialViewAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]\n  public sealed class AspMvcSuppressViewErrorAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.\n  /// Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcDisplayTemplateAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template.\n  /// Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcEditorTemplateAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template.\n  /// Use this attribute for custom wrappers similar to\n  /// <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcTemplateAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter\n  /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly\n  /// from the context. Use this attribute for custom wrappers similar to\n  /// <c>System.Web.Mvc.Controller.View(Object)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcViewAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter\n  /// is an MVC view component name.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcViewComponentAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter\n  /// is an MVC view component view. If applied to a method, the MVC view component view name is default.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class AspMvcViewComponentViewAttribute : Attribute { }\n\n  /// <summary>\n  /// ASP.NET MVC attribute. When applied to a parameter of an attribute,\n  /// indicates that this parameter is an MVC action name.\n  /// </summary>\n  /// <example><code>\n  /// [ActionName(\"Foo\")]\n  /// public ActionResult Login(string returnUrl) {\n  ///   ViewBag.ReturnUrl = Url.Action(\"Foo\"); // OK\n  ///   return RedirectToAction(\"Bar\"); // Error: Cannot resolve action\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]\n  public sealed class AspMvcActionSelectorAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]\n  public sealed class HtmlElementAttributesAttribute : Attribute\n  {\n    public HtmlElementAttributesAttribute() { }\n\n    public HtmlElementAttributesAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [CanBeNull] public string Name { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class HtmlAttributeValueAttribute : Attribute\n  {\n    public HtmlAttributeValueAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; }\n  }\n\n  /// <summary>\n  /// Razor attribute. Indicates that the marked parameter or method is a Razor section.\n  /// Use this attribute for custom wrappers similar to\n  /// <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]\n  public sealed class RazorSectionAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates how method, constructor invocation, or property access\n  /// over collection type affects the contents of the collection.\n  /// Use <see cref=\"CollectionAccessType\"/> to specify the access type.\n  /// </summary>\n  /// <remarks>\n  /// Using this attribute only makes sense if all collection methods are marked with this attribute.\n  /// </remarks>\n  /// <example><code>\n  /// public class MyStringCollection : List&lt;string&gt;\n  /// {\n  ///   [CollectionAccess(CollectionAccessType.Read)]\n  ///   public string GetFirstString()\n  ///   {\n  ///     return this.ElementAt(0);\n  ///   }\n  /// }\n  /// class Test\n  /// {\n  ///   public void Foo()\n  ///   {\n  ///     // Warning: Contents of the collection is never updated\n  ///     var col = new MyStringCollection();\n  ///     string x = col.GetFirstString();\n  ///   }\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)]\n  public sealed class CollectionAccessAttribute : Attribute\n  {\n    public CollectionAccessAttribute(CollectionAccessType collectionAccessType)\n    {\n      CollectionAccessType = collectionAccessType;\n    }\n\n    public CollectionAccessType CollectionAccessType { get; }\n  }\n\n  /// <summary>\n  /// Provides a value for the <see cref=\"CollectionAccessAttribute\"/> to define\n  /// how the collection method invocation affects the contents of the collection.\n  /// </summary>\n  [Flags]\n  public enum CollectionAccessType\n  {\n    /// <summary>Method does not use or modify content of the collection.</summary>\n    None = 0,\n    /// <summary>Method only reads content of the collection but does not modify it.</summary>\n    Read = 1,\n    /// <summary>Method can change content of the collection but does not add new elements.</summary>\n    ModifyExistingContent = 2,\n    /// <summary>Method can add new elements to the collection.</summary>\n    UpdatedContent = ModifyExistingContent | 4\n  }\n\n  /// <summary>\n  /// Indicates that the marked method is assertion method, i.e. it halts the control flow if\n  /// one of the conditions is satisfied. To set the condition, mark one of the parameters with\n  /// <see cref=\"AssertionConditionAttribute\"/> attribute.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class AssertionMethodAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates the condition parameter of the assertion method. The method itself should be\n  /// marked by <see cref=\"AssertionMethodAttribute\"/> attribute. The mandatory argument of\n  /// the attribute is the assertion type.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class AssertionConditionAttribute : Attribute\n  {\n    public AssertionConditionAttribute(AssertionConditionType conditionType)\n    {\n      ConditionType = conditionType;\n    }\n\n    public AssertionConditionType ConditionType { get; }\n  }\n\n  /// <summary>\n  /// Specifies assertion type. If the assertion method argument satisfies the condition,\n  /// then the execution continues. Otherwise, execution is assumed to be halted.\n  /// </summary>\n  public enum AssertionConditionType\n  {\n    /// <summary>Marked parameter should be evaluated to true.</summary>\n    IS_TRUE = 0,\n    /// <summary>Marked parameter should be evaluated to false.</summary>\n    IS_FALSE = 1,\n    /// <summary>Marked parameter should be evaluated to null value.</summary>\n    IS_NULL = 2,\n    /// <summary>Marked parameter should be evaluated to not null value.</summary>\n    IS_NOT_NULL = 3,\n  }\n\n  /// <summary>\n  /// Indicates that the marked method unconditionally terminates control flow execution.\n  /// For example, it could unconditionally throw exception.\n  /// </summary>\n  [Obsolete(\"Use [ContractAnnotation('=> halt')] instead\")]\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class TerminatesProgramAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select,\n  /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters\n  /// of delegate type by analyzing LINQ method chains.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class LinqTunnelAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that IEnumerable passed as a parameter is not enumerated.\n  /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection.\n  /// </summary>\n  /// <example><code>\n  /// static void ThrowIfNull&lt;T&gt;([NoEnumeration] T v, string n) where T : class\n  /// {\n  ///   // custom check for null but no enumeration\n  /// }\n  /// \n  /// void Foo(IEnumerable&lt;string&gt; values)\n  /// {\n  ///   ThrowIfNull(values, nameof(values));\n  ///   var x = values.ToList(); // No warnings about multiple enumeration\n  /// }\n  /// </code></example>\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class NoEnumerationAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the marked parameter is a regular expression pattern.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]\n  public sealed class RegexPatternAttribute : Attribute { }\n\n  /// <summary>\n  /// Prevents the Member Reordering feature from tossing members of the marked class.\n  /// </summary>\n  /// <remarks>\n  /// The attribute must be mentioned in your member reordering patterns.\n  /// </remarks>\n  [AttributeUsage(\n    AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)]\n  public sealed class NoReorderAttribute : Attribute { }\n\n  /// <summary>\n  /// XAML attribute. Indicates the type that has <c>ItemsSource</c> property and should be treated\n  /// as <c>ItemsControl</c>-derived type, to enable inner items <c>DataContext</c> type resolve.\n  /// </summary>\n  [AttributeUsage(AttributeTargets.Class)]\n  public sealed class XamlItemsControlAttribute : Attribute { }\n\n  /// <summary>\n  /// XAML attribute. Indicates the property of some <c>BindingBase</c>-derived type, that\n  /// is used to bind some item of <c>ItemsControl</c>-derived type. This annotation will\n  /// enable the <c>DataContext</c> type resolve for XAML bindings for such properties.\n  /// </summary>\n  /// <remarks>\n  /// Property should have the tree ancestor of the <c>ItemsControl</c> type or\n  /// marked with the <see cref=\"XamlItemsControlAttribute\"/> attribute.\n  /// </remarks>\n  [AttributeUsage(AttributeTargets.Property)]\n  public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { }\n\n  /// <summary>\n  /// XAML attribute. Indicates the property of some <c>Style</c>-derived type, that\n  /// is used to style items of <c>ItemsControl</c>-derived type. This annotation will\n  /// enable the <c>DataContext</c> type resolve for XAML bindings for such properties.\n  /// </summary>\n  /// <remarks>\n  /// Property should have the tree ancestor of the <c>ItemsControl</c> type or\n  /// marked with the <see cref=\"XamlItemsControlAttribute\"/> attribute.\n  /// </remarks>\n  [AttributeUsage(AttributeTargets.Property)]\n  public sealed class XamlItemStyleOfItemsControlAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n  public sealed class AspChildControlTypeAttribute : Attribute\n  {\n    public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType)\n    {\n      TagName = tagName;\n      ControlType = controlType;\n    }\n\n    [NotNull] public string TagName { get; }\n\n    [NotNull] public Type ControlType { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]\n  public sealed class AspDataFieldAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]\n  public sealed class AspDataFieldsAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Property)]\n  public sealed class AspMethodPropertyAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n  public sealed class AspRequiredAttributeAttribute : Attribute\n  {\n    public AspRequiredAttributeAttribute([NotNull] string attribute)\n    {\n      Attribute = attribute;\n    }\n\n    [NotNull] public string Attribute { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Property)]\n  public sealed class AspTypePropertyAttribute : Attribute\n  {\n    public bool CreateConstructorReferences { get; }\n\n    public AspTypePropertyAttribute(bool createConstructorReferences)\n    {\n      CreateConstructorReferences = createConstructorReferences;\n    }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class RazorImportNamespaceAttribute : Attribute\n  {\n    public RazorImportNamespaceAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class RazorInjectionAttribute : Attribute\n  {\n    public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName)\n    {\n      Type = type;\n      FieldName = fieldName;\n    }\n\n    [NotNull] public string Type { get; }\n\n    [NotNull] public string FieldName { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class RazorDirectiveAttribute : Attribute\n  {\n    public RazorDirectiveAttribute([NotNull] string directive)\n    {\n      Directive = directive;\n    }\n\n    [NotNull] public string Directive { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]\n  public sealed class RazorPageBaseTypeAttribute : Attribute\n  {\n      public RazorPageBaseTypeAttribute([NotNull] string baseType)\n      {\n        BaseType = baseType;\n      }\n      public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName)\n      {\n          BaseType = baseType;\n          PageName = pageName;\n      }\n\n      [NotNull] public string BaseType { get; }\n      [CanBeNull] public string PageName { get; }\n  }\n\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class RazorHelperCommonAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Property)]\n  public sealed class RazorLayoutAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class RazorWriteLiteralMethodAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class RazorWriteMethodAttribute : Attribute { }\n\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class RazorWriteMethodParameterAttribute : Attribute { }\n}"
  },
  {
    "path": "Diz.App.Winforms/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.App.Winforms;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizAppWinformsCompositionRoot))]"
  },
  {
    "path": "Diz.App.Winforms/Properties/PublishProfiles/FolderProfile.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>bin\\Release\\net9.0-windows\\publish\\</PublishDir>\n        <PublishProtocol>FileSystem</PublishProtocol>\n        <TargetFramework>net9.0-windows</TargetFramework>\n        <SelfContained>false</SelfContained>\n        <DeleteExistingFiles>True</DeleteExistingFiles>\n    </PropertyGroup>\n</Project>"
  },
  {
    "path": "Diz.App.Winforms/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Diz.App.Winforms.Properties {\n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"4.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"DiztinGUIsh.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Diz.App.Winforms/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n</root>"
  },
  {
    "path": "Diz.App.Winforms/RegisterWinformsServices.cs",
    "content": "using Diz.App.Common;\nusing Diz.Core.util;\nusing Diz.Ui.Winforms;\nusing LightInject;\n\nnamespace Diz.App.Winforms;\n\npublic static class DizWinformsRegisterServices\n{\n    public static IServiceFactory CreateServiceFactoryAndRegisterTypes()\n    {\n        var serviceProvider = DizServiceProvider.CreateServiceContainer();\n        RegisterDizUiServices(serviceProvider);\n        \n        return serviceProvider;\n    }\n\n    public static void RegisterDizUiServices(IServiceRegistry serviceRegistry)\n    {\n        // option #1: we can simply register services in any Diz*dll's that are found in a scan.\n        // this is easy but we have less control\n        // DizCoreServicesDllRegistration.RegisterServicesInDizDlls(serviceRegistry);\n\n        // option #2: register everything by hand (this is what we'll do).\n        \n        // pull in all common stuff (platform-independent)\n        serviceRegistry.RegisterFrom<DizAppCommonCompositionRoot>();\n        \n        // pull in winforms-specific UI stuff:\n        serviceRegistry.RegisterFrom<DizUiWinformsCompositionRoot>();\n        \n        // finally, pull in OUR stuff, which is winforms-specific\n        serviceRegistry.RegisterFrom<DizAppWinformsCompositionRoot>();\n    }\n}"
  },
  {
    "path": "Diz.App.Winforms/build/TidyPublishDir.targets",
    "content": "<!-- \nThis file is invoked for publish targets \nOn publish, move some files around to make the distribution distro look nicer.\n\nreference info:\nhttps://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-extend-the-visual-studio-build-process?view=vs-2019\nhttps://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/visual-studio-publish-profiles?view=aspnetcore-5.0\n-->\n<Project>\n    <Target Name=\"TidyPublishDir\" AfterTargets=\"Publish\">\n        <!--<Message Text=\"xxxxxxxx Custom target after publish $(PublishDir) ****************\" Importance=\"high\" />-->\n        \n        <Error Condition=\"'$(PublishDir)' == ''\" Text=\"PublishDir variable not set, can't continue.\"/>\n\n        <!-- where we're going to move DLLs and other root-level things we don't want in the root dir anymore -->\n        <PropertyGroup>\n            <LibOutDir>$(PublishDir)lib\\</LibOutDir>\n            <DocsOutDir>$(PublishDir)docs\\</DocsOutDir>\n            <NewExeName>DIZ.exe</NewExeName>\n        </PropertyGroup>\n\n        <!-- list of *static* files to copy into the root dir -->\n        <ItemGroup>\n            <ExtraStaticFiles Include=\"dist\\**\\*.*\"/>\n            <!-- Don't include .md files here anymore, they'll be handled separately -->\n        </ItemGroup>\n\n        <!-- list of .md files to copy into the docs folder -->\n        <ItemGroup>\n            <MdFiles Include=\"..\\README.md\"/>\n            <MdFiles Include=\"..\\LICENSE.md\"/>\n        </ItemGroup>\n\n        <!-- create a list of files we want to move in the output lib folder -->\n        <ItemGroup>\n            <LibFiles Include=\"$(PublishDir)**\\*.dll\"/>\n            <LibFiles Include=\"$(PublishDir)**\\*.xml\"/>\n<!--   MINIFYING THIS, so, exclude these         <LibFiles Include=\"$(PublishDir)**\\*.pdb\"/>-->\n<!--   NORMALLY YES. for Diz, it's OK to not have these as long as our code defaults are OK         <LibFiles Include=\"$(PublishDir)**\\*.config\"/>-->\n            <LibFiles Remove=\"$(LibOutDir)\\**\\*.*\"/>\n            <LibFiles Remove=\"$(PublishDir)$(ProjectName)*\"/>\n            <LibFiles Remove=\"$(PublishDir)$(ProjectName).exe\"/>\n        </ItemGroup>\n\n        <!-- list of files to delete from the publish directory -->\n        <ItemGroup>\n            <FilesToDelete Include=\"$(PublishDir)**\\*.pdb\"/>\n            <FilesToDelete Include=\"$(PublishDir)**\\*.config\"/>\n        </ItemGroup>\n\n        <!-- move file to lib/ dir -->\n        <!-- note: THIS <Move> command is fine, but, we need to modify our manifests to search this new folder.\n            works in net4.8 but not 5.0 yet.\n        <Move SourceFiles=\"%(LibFiles.Identity)\" DestinationFolder=\"$(LibOutDir)%(LibFiles.RecursiveDir)\"/>\n        -->\n\n        <!-- copy any static files to the root dir, keep the same folder structure -->\n        <Copy SourceFiles=\"%(ExtraStaticFiles.Identity)\" DestinationFolder=\"$(PublishDir)%(ExtraStaticFiles.RecursiveDir)\"/>\n\n        <!-- copy .md files to the docs folder -->\n        <MakeDir Directories=\"$(DocsOutDir)\" Condition=\"@(MdFiles) != ''\" />\n        <Copy SourceFiles=\"%(MdFiles.Identity)\" DestinationFolder=\"$(DocsOutDir)\" Condition=\"@(MdFiles) != ''\" />\n\n        <!-- rename the main executable -->\n        <Move SourceFiles=\"$(PublishDir)$(ProjectName).exe\" DestinationFiles=\"$(PublishDir)$(NewExeName)\" Condition=\"Exists('$(PublishDir)$(ProjectName).exe')\" />\n\n        <!-- delete unwanted files -->\n        <Delete Files=\"@(FilesToDelete)\" ContinueOnError=\"true\" />\n\n        <!-- done moving stuff. remove any directories that are now-empty left -->\n        <ItemGroup>\n            <Directories Include=\"$([System.IO.Directory]::GetDirectories('$(PublishDir)', '*', System.IO.SearchOption.AllDirectories))\"/>\n            <Directories>\n                <Files>$([System.IO.Directory]::GetFiles(\"%(Directories.Identity)\", \"*\", System.IO.SearchOption.AllDirectories).get_Length())</Files>\n            </Directories>\n        </ItemGroup>\n        <RemoveDir Directories=\"@(Directories)\" Condition=\"%(Files)=='0'\"/>\n    </Target>\n</Project>"
  },
  {
    "path": "Diz.App.Winforms/dist/docs/HELP.md",
    "content": "﻿DiztinGUIsh Introduction\n------------------------\n\nDiztinGUIsh is a Super NES ROM disassembler. Open a ROM file, flag each byte as code or data, add some extra info like labels and comments, \nand disassemble. The output will be one or more .asm files that are (idealy) equivalent to the original source code of the game. \nThe assembly files can then be re-assembled with [Asar](https://github.com/RPGHacker/asar/releases) to check for integrity,\nand/or to make simple changes to the code.\n\nAs you may guess, tagging every single byte as data or code is a tedious process, so DiztinGUIsh has many tools built in to make the \nprocess more bearable. Disassembling 65C816 code can be quite tricky, so some human input is still required to get a 100% accurate disassembly, but most of the job will be done automatically.\n\nDiz works GREAT with Tracelog tools, like BSNES+. We modified a version of BSNES to also support live capture from a running BSNES,\nwhich means you can run the game at full framerate and grab all the juicy data coming off the CPU. This will mark millions of instructions\ncorrect with the right flags for you, and is a fantastic way to crack a ROM's secrets wide open.\n\nBe sure to also check out BSNES's usage map importing features to fill in sections of data vs code.\n\n* * *\n\nDiztinGUIsh Main Window\n-----------------------\n\nThis is the window where most of the work is done. It consists of one very large table, some text at the bottom, and some options at the top.\n\n#### File\n\nCreate new projects, open & save [projects](#projectfiles), [export a disassembly](#disassembly), and exit from this menu.\n\n#### Tools\n\nThis menu will bring up additional tools to help you with managing data.\n\n*   **Visual Map**: This window lets you see a visual map of the entire ROM, including what each byte is flagged as.\n*   **Constants**: This changes what base the \"raw byte\" column is shown in. You can pick from decimal, hexadecimal, and binary.\n*   **Options**\n    *   **Move With Step**: When enabled, the currently selected cell in the table will jump to the byte after the last modified byte after a auto step or mark many operation.\n\n* * *\n\n### Project Files\n\nAll of the flags, labels, comments, and other data you make to help with disassembling will be put into a single [Project File](#fileformat) (\\*.diz). \nThis file contains a _link_ to the ROM you are currently working on, but doesn't contain the ROM itself. This is for copyright reasons, and so you can\neasily share project files without worry of sharing ROM files as well. Because of this, if the ROM file you are working on is deleted or is relocated on \nyour computer, DiztinGUIsh will ask to relocate the file. The ROM's internal name and checksum are stored to make sure you relink the same ROM you started \nwith, but it is technically possible to relink a different ROM. Not sure what would happen then, but it wouldn't be anything you'd want.\n\nWhen you start DiztinGUIsh, you can start a new project or open an existing one. Opening an existing project is simple, though you may have to relink \nyour ROM if it moved, or if you received this project file from a friend. When you start a new project, DiztinGUIsh will first prompt you to select a \nROM file you want to work on.\n\n#### Starting a Project\n\nDiztinGUIsh currently supports seven types of _ROM Mapping Modes_: LoROM, HiROM, two SA-1 modes, SuperFX, ExHiROM, & ExLoROM. The program will try its best \nto detect what mode the ROM you selected uses. There is a chance it won't be able to figure it out, or it will guess wrong. After you select a ROM file,\nDiztinGUIsh will show the ROM's internal name. If this doesn't look correct, try changing the ROM Map mode.\n\n_Note: Some Japanese titles will display as gibberish, so it is possible that the correct ROM Mapping Mode will result in a garbage looking title._\n\n_Note: Although mapping mode 2, Super MMC, is an option, it currently functions identically to HiROM._\n\nDiztinGUIsh also provides a few extra jumpstart options on the New Project window. \nThe table of numbers on the bottom half of the window show the ROM's _vectors_, which are special pointers that locate code that runs under special \ncircumstances (interrupts, bootup, etc.). By default, DiztinGUIsh will check off the vectors it thinks are important. Checking off a vector will \nautomatically generate a label at the location of the pointer (e.g. \"Native\\_NMI\").\n\nIf the final checkbox is checked, DiztinGUIsh will automatically mark flags for the entire internal header. This includes the internal name, \nROM meta info & mapping settings, developer codes, & the vectors.\n\n* * *\n\n### Table Grid View\n\nIn DiztinGUIsh, _one byte equals one row in the table_. Instructions will be one or more rows depending on how many operands there are. \nPointers will be more than one row. Each byte has many pieces of data associated with it--these are stored in each of the table's columns.\n\n#### Label\n\nHere you can create a custom label for this location in the ROM. Labels are useful to identify what things are in the ROM. \nYou can label code, data, or anything really. Labels will be automatically used in the disassembly when available.\n\n#### PC\n\nThis column shows the \"program counter\" for the current byte. This is effectively the location in the SNES's address space where this byte will be found.\n\n#### @\n\nThis column shows an ASCII representation of the current byte. Mainly useful for locating text within the ROM.\n\n#### #\n\nThis column shows the raw byte. You can change what base this value is displayed in via the View -> Constants menu.\n\n#### <\\*>\n\nThis column shows _in points, out points, end points, & read points_.\n\n*   In points are locations in ROM where execution can jump to from somewhere other than the instruction directly before it. These are denoted by a \">\" symbol. \n    In points are usually the result of a branch, jump, or call instruction.\n*   Out points are locations where execution can jump elsewhere other than the instruction directly after it. These are denoted by a \"<\" symbol. \n    Out points are usually the result of a branch, jump, call, or return instruction.\n*   End points are special out points where execution cannot directly flow from this instruction to the next directly after. \n    These are denoted by a \"X\" symbol. End points are usually the result of a jump or return instruction.\n*   Finally, read points are locations in ROM that are the intermediate address of some other load/store/math instruction in the ROM. \n    These are denoted by a \"\\*\" symbol. Read points are often the start of a table of data.\n\n#### Instruction\n\nThis column shows what instruction this byte would be disassembled to if it were treated as an opcode.\n\nThe instruction may be highlighted in yellow if DiztinGUIsh thinks the instruction is risky. That is, a rarely used instruction that more likely \nthan not means that the code is desynched.\n\n#### IA\n\nThis column shows the intermediate address of the instruction or pointer located at this byte. The intermediate address is basically the\n\"address of importance\" of an instruction--what address is actually being read/stored/jumped to, etc. Often, the instruction operands only \nspecify a small chunk of the intermediate address, and the rest of it has to be inferred by other registers such as the program counter, \ndata bank register, or direct page register. This column does all the math for you and spits out the intermediate address of the instruction. \nNote that this is not to be confused with the _effective address_, which is the \"final result\" address after locating indirect operands and/or\nadding index registers.\n\nThe intermediate address will be calculated assuming this byte is marked as an opcode--UNLESS it is marked as some sort of pointer. Then, \nthe intermediate address will just be the value of the pointer.\n\n_Note: If the byte is marked as a 16-bit pointer, the bank of the intermediate address will be derived from the Data Bank register (B)._\n\n#### Flag\n\nThis column displays what kind of data this byte will be treated as. Your primary job with Diz is to  \ncorrectly mark the ROM with these flags\n\n*   **Unreached**: This is the default flag. Basically means unknown. Your goal in Diz is clear all (or enough) unreached sections\n*   **Opcode**: This byte is the opcode of an instruction. Zero or more Operands may follow it.\n*   **Operand**: This byte is an operand of an instruction. Which instruction? The one marked as an Opcode a byte or more before it.\n*   **Data (8-bit)**: Generic 8-bit (1-byte) long data.\n*   **Graphics**: Special 8-bit data specifically used as graphics. Isn't treated any differently than plain 8-bit data (_yet_).\n*   **Music**: Special 8-bit data specifically used as music. Isn't treated any differently than plain 8-bit data (_yet_).\n*   **Empty**: Special 8-bit data specifically used as empty filler. Isn't treated any differently than plain 8-bit data (_yet_).\n*   **Data (16-bit)**: Generic 16-bit (2-byte) long data.\n*   **Pointer (16-bit)**: The lower 16-bits of a pointer. The bank of the intermediate address is derived from the Data Bank register (B).\n*   **Data (24-bit)**: Generic 24-bit (3-byte) long data.\n*   **Pointer (24-bit)**: A full 24-bit pointer.\n*   **Data (32-bit)**: Generic 32-bit (4-byte) long data.\n*   **Pointer (32-bit)**: A full 24-bit pointer, followed by a filler byte. This is common so that pointers are separated by 4 bytes (a power of 2).\n*   **Text**: Special 8-bit data specifically used as ASCII text. This will be disassembled into a human-readable string.\n\n#### B\n\nThe value of the _Data Bank register_ during execution of this code. Some instructions derive their intermediate address using the value of the data bank register.\nThe data bank register is also used as the bank byte of a 16-bit pointer.\n\nThe value will be highlighted in yellow if the instruction at this location stores the data bank register (PHB). It will be highlighted in red if the\ninstruction writes to the data bank register (PLB : MVP : MVN). This way you can be on a lookout for changes to this register.\n\n#### D\n\nThe value of the _Direct Page register_ during execution of this code. Some instructions derive their intermediate address using the value of the direct\npage register.\n\nThe value will be highlighted in yellow if the instruction at this location stores the direct page register (PHD : TDC). It will be highlighted in red \nif the instruction writes to the direct page register (PLD : TCD). This way you can be on a lookout for changes to this register.\n\n#### M\n\nThe current state of the M flag in the program status register (P). When the flag is cleared (m = 0), the accumulator A is 16 bits wide. \nWhen it is set (M = 1), the accumulator A is 8 bits wide. Some instructions interpret their operands differently according to the state of the M flag. \nIf some code is desynched or doesn't make since, try toggling the M flag.\n\nThe value will be highlighted in yellow if the instruction at this location stores the program status register (PHP).\nIt will be highlighted in red if the instruction writes to the program status register and potentially modifies the M flag (PLP : REP : SEP). \nThis way you can be on a lookout for changes to this flag.\n\n#### X\n\nThe current state of the X flag in the program status register (P). When the flag is cleared (x = 0), the index registers X and Y are 16 bits wide.\nWhen it is set (X = 1), the index registers X and Y are 8 bits wide. Some instructions interpret their operands differently according to the state \nof the X flag. If some code is desynched or doesn't make since, try toggling the X flag.\n\nThe value will be highlighted in yellow if the instruction at this location stores the program status register (PHP). It will be highlighted in red\nif the instruction writes to the program status register and potentially modifies the X flag (PLP : REP : SEP). This way you can be on a lookout for\nchanges to this flag.\n\n#### Comment\n\nHere you can write notes about the project. These comments will be output into the disassembly, but _only on bytes that start a line_.\nFor example, a comment on a byte that is marked as an opcode will be written, but a comment on an operand byte will not be written.\n\n#### Status Bar\n\nThe status bar at the bottom of the window will show the number of bytes reached, and the currently selected marking flag.\n\n#### Navigation\n\nUse the arrow keys to move to different cells. Page Up & Page Down will move 16 rows at a time, and Home & End will move 256 rows at a time.\n\nCells you can type in are highlighted in green.\n\nIntermediate addresses of the currently selected cell will be highlighted in pink. So will instructions whose intermediate addresses are the \ncurrently selected cell.\n\n* * *\n\n### Stepping\n\n**Hotkey: S, I**\n\nStepping is the method of marking bytes as code. Instead of marking each byte as opcode and operand separately, you can step through the instruction, \nand DiztinGUIsh will automatically assign opcode and operand flags as necessary.\n\nStepping an instruction will automatically copy the D, B, M, & X values from the previous instruction. If a REP or SEP instruction is stepped, \nthe M and X values will be modified as necessary.\n\n_Note: Most trace logs will reflect changes to the D, B, M, & X values on the instruction after the one that modifies the register. However, \nDiztinGUIsh likes to show the updated values on the same row as the instruction that updates the values._\n\nThere are two stepping commands: step, and step in. Step will advance the selector to the instruction directly following this instruction. \nStep in will advance to the intermediate address of this instruction. This is useful for taking a branch or jumping into a routine call. \nNote that plain step will still step in if the instruction is a jump instruction.\n\n* * *\n\n### Auto Stepping\n\n**Hotkey: A, Ctrl + A**\n\nThere is a lot of code in a ROM, so stepping through each instruction can be time consuming. Therefore, this option will let you step through \ninstructions automatically until something happens.\n\n#### Auto Step (Safe)\n\nThis is the recommended way of auto stepping. When auto step hits a branch, it will not take it. When it hits a jump, it will jump. When it\nhits a function call, it will save the program counter and jump to the routine. When it hits a return, it will try to restore the program\ncounter to what it was before the call. It also saves and restores the value of the program status register and the M and X flags.\n\nBasically, it pretends to execute code until it hits something it can't do. Some jump instructions refer to values in RAM, which are not kept\ntrack of. At this point, the auto step will pause and wait for you to continue it at a point that makes sense.\n\nAuto Step will also stop if it hits a branch or jump that it has already seen in one go. This is to prevent it getting stuck on infinite loops.\nIt will also stop if it hits a risky instruction--that is, a rare instruction that more likely than not means that the code is desynched.\nIt will also stop if it hits an instruction that is already marked as something other than code.\n\n#### Auto Step (Harsh)\n\nThis auto step will rush through bytes like a freight train and disassemble them one directly after the other. It doesn't care about jumps,\nbranches, or anything. The only smart thing it does is updates the M and X flags upon stepping through REP and SEP instructions.\n\nBecause of how hardy it is, it will never stop if it hits something risky. You have to specify how many bytes to disassemble. It will also\nclobber over stuff that is already marked as something other than code.\n\n* * *\n\n### Goto\n\n**Hotkey: Ctrl + G, T, U, H, N, F3**\n\nThis is an easy way to hop around the ROM without having to scroll all the way to where you want.\n\nHitting Ctrl + G will bring up the [Goto window](#gotowindow). You can type in a SNES address or ROM file offset in decimal or hex to go directly to that location.\n\nYou can press T to jump to the intermediate address of the currently selected instruction. Only if the intermediate address refers to ROM of course.\n\nYou can press U, H, or N to jump to the first unreached byte of the project, the nearest unreached block behind the selected address, or \nthe nearest unreached block ahead of the selected address. These are useful just to get somewhere you haven't been yet.\n\nThe History window is a record of your recent activity in the project.  You probably want to be best friends with ALT+LEFT and ALT+RIGHT while stepping,\nsince branches can jump you all over the codebase and you can lose your train of thought\n\nPress F3 to go to the \"Next Unreached In Point\". Handy when working with tracelog imports, which fill in a LOT of opcodes but leave small branches unmarked \nthat are rarely taken by normal gameplay.\n\nExample:\n![image](https://github.com/IsoFrieze/DiztinGUIsh/assets/5413064/5910ca30-4d54-4d19-8dc8-5c8d292a2e65)\n\n* * *\n\n### Marking\n\n**Hotkey: K, Ctrl + K, B, D, M, X**\n\nFor anything that isn't code, you will have to mark manually. You can select which flag type to mark with under the Edit -> Select Marker menu. \nThe currently selected flag type is shown in the status bar at the bottom of the window.\n\nPressing K will mark a single instance of that flag type. That is, 8-bit data will mark 1 byte at a time, 16-bit data 2 bytes at a time, etc.\n\nYou can press Ctrl + K to bring up a window that lets you mark an entire range at once. You can specify SNES address or ROM file offset, \nin hex or decimal, and you can specify the range via start & end points, or just the number of bytes to mark.\n\nPressing B, D, M, or X will jump the selected cell to the data bank, direct page, M flag, or X flag cell respectively. You can hold Ctrl while \npressing one of these keys to bring up the same window as Ctrl + K with default options to mark that value.\n\n* * *\n\n### Labels and Comments\n\n**Hotkey: L, C**\n\nPressing L will jump the selected cell to the label cell automatically so you can add a label to this byte. \nPressing C will do the same for the comment column.\n\n#### Valid Labels\n\nAsar has a limitation on what characters can make up a label, and DiztinGUIsh follows the same format. Currently, \nlabels must only contain the following characters:\n\na-z A-Z 0-9 \\_\n\nCharacters within a label that don't fit this description will be converted to \\_s.\n\nDiztinGUIsh currently does not support sublabels (starting with a .), or +/- labels (made of \\+ - characters).\n\nDiztinGUIsh also currently does not check for duplicate labels; that is, two more more addresses with the same label. \nFor now, please use the [Label List](#labellist) to check for any duplicate labels.\n\n* * *\n\n### Misalignments & Desynching\n\n**Hotkey: Ctrl + F**\n\n_Desynching_ refers to incorrect instructions being disassembled due to opcodes being marked as operands and vice versa. \nSome instructions have a different amount of operand bytes associated with them depending on the state of the M and X flags. \nIf these flags are assumed incorrectly, the size of the instruction will be assumed incorrectly, causing a byte that should \nbe an operand being treated as an opcode or vice versa.\n\nSometimes desynched instructions are hard to catch. In fact, they can be disassembled and reassembled, and you wouldn't even notice. \nThe SNES CPU would just execute the code as it would normally; it just wouldn't match what the disassembly says. \nFor example, take the following bytecode: A9 00 8D 85 00. This could be LDA #$00 : STA $0085 or it could be LDA #$8D00 : STA $00.\nBoth are perfectly legitimate, but only one is correct.\n\nA common cause of desynched code in DiztinGUIsh has to do with stepping through code while the X and M flags are incorrect. \nSometimes, bytes can be marked as opcodes and operands incorrectly that accidentally overwrite already marked bytes.\n\nIn DiztinGUIsh, _misalignment_ refers to bytes that are marked incorrectly, due to the length of the instruction or data not\nmatching the flags. For example, the TAX instruction has no operands. But if the byte following this instruction is marked as \nan operand, this is a misalignment. Misalignments can also occur with data and pointers. If there is a block of 9 bytes marked \nas 16-bit data, there is a misalignment somewhere since the number of bytes is odd.\n\nThe Misaligned Flags checker window will look for misalignments. You can just scan the ROM without actually modifying anything.\nMisalignments will be identified and output into the text box so you can correct them manually. You can also just have DiztinGUIsh \nattempt to fix all misalignments automatically. It uses a pretty brute force method, so it may not produce correct results, \nbut at least it will get rid of all misalignments.\n\nIt is recommended to scan for misalignments before outputting a disassembly.\n\n* * *\n\n### In/Out/End/Read Points\n\n**Hotkey: Ctrl + P**\n\nAs DiztinGUIsh steps through instructions, it will mark [in points, out points, end points, and read points](#tablegrid).\nHowever, marking bytes as opcode and operand manually will not produce these points. Importing CDLs will also not generate points. \nAlso, once a byte is marked with a point, you can't unmark it.\n\nThe Rescan for In/Out Points window will clear all points and readd them using the current flags.\n\nIt is recommended to rescan for in/out/end/read points before outputting a disassembly.\n\n* * *\n\n### Keyboard Hotkeys\n\nPretty much everything has a hotkey associated with it, so you can use the entire program with the keyboard.\nNote that hotkeys won't work when the selected cell is editable, as typing anything will just put it into the box.\n\n* * *\n\nGoto Window\n-----------\n\nUsing this window you can select any byte in the ROM without having to scroll all the way to it.\n\n* * *\n\n### SNES Address vs ROM file Offset\n\nThe SNES address is the location in the SNES's address space where the byte is located.\n\nThe ROM File Offset (also called the PC offset) is the raw offset of the byte in the ROM file. \nThe first byte has an offset of 0. The ROM is always treated as unheadered, even if the ROM linked to the project has an SMC header.\n\n* * *\n\n### Hexadecimal vs Decimal\n\nHexadecimal is a base 16 counting system. It is what most hex editors use for displaying offsets, since 16 is a power of 2.\n\nDecimal is a base 10 counting system.\n\n_Note: Using decimal to specify the SNES address is silly, I've found._\n\n* * *\n\nMark Many Window\n----------------\n\nThis window lets you set flags and registers for more than one byte at a time.\n\n* * *\n\n### Property & Value\n\nYou can choose which property to modify, and what value to give for the entire specified range.\n\nThe property field will default to a different setting depending on what hotkey you used to open the window.\n\n* * *\n\n### Address Range\n\nSee the [Goto Window](#gotowindow) for info on ROM vs PC and Hex vs Dec.\n\nChanging the bounds of the range will automatically update the number of bytes and vice versa. If the values you input\ngo past the end of the ROM, they will snap to the end of the ROM.\n\n* * *\n\nFix Misaligned Flags Window\n---------------------------\n\nSee [Misalignments & Desynching](#desynching).\n\n* * *\n\nRescan for In/Out Points Window\n-------------------------------\n\nSee [In/Out/End/Read Points](#inoutpoints).\n\n* * *\n\nLabel List Window\n-----------------\n\nThis window shows every single label that has been created, and the address it is associated with. \nLabels in this list will automatically be updated, added, or removed when they are modified via the Main Window.\nYou can also add, remove, or edit them from this window. The labels can be sorted by address or name by clicking on the table header.\n\n#### Jump to\n\nClicking on this button will jump to the location of this label in the Main Window.\n\n#### Import...\n\nUse this to import a \\*.csv file with a list of labels in the format described below. \nThis is useful if you have an external program that generates labels for you.\n\nWe also support importing from BSNES's symbol file format.\n\n#### Export...\n\nThis will export a \\*.csv file with all of the current labels. The format is quite simple; each label is one line in the CSV file, formatted as such:\n\nsnes\\_address,label\\_name\n\nNote: Since neither the address nor the label should contain spaces or quotation marks, DiztinGUIsh will expect no quotation marks surrounding either field.\n\n#### Label Table\n\nPretty self-explanitory--the first column is the SNES address that the label name in the second column corresponds to.\n\nYou can modify labels by double-clicking on them. Hitting the del key will delete a label. You can even select more than one at once to delete.\nTo add a label, just add the address and label name to the empty row at the end of the table. Make sure the address is a valid 24-bit address \nin hexadecimal, that the address you enter isn't already in the list, and that the label name contains only valid characters. \nSee [here](labelcomment) for what makes a valid label name.\n\nSince DiztinGUIsh currently does not prevent you from reusing a label name more than once, it will warn you of duplicates by \nhighlighting the rows that contain duplicate label names in yellow.\n\n* * *\n\nExport Disassembly Window\n-------------------------\n\nThis window lets you set the options for outputting a disassembly that can reassembled by Asar.\n\n* * *\n\n### Output Format\n\nThis box lets you choose how each line of the disassembly will be formatted. There are several types of arguments you can specify. Arguments are put inbetween percent signs (%). Some arguments can be followed by a length parameter, which is defined by a colon (:) and then a number. A positive number will align the argument to the left; negative will align to the right.\n\n*   **\\***: Anything that isn't an argument will be output directly into the line.\n*   **%%**: Output a percent sign.\n*   **%label:length%**: Output the label for this line. Default length of -22.\n*   **%code:length%**: Output the instruction, data, pointer, etc. for this line. This is where assembler directives will appear as well. Default length of 37.\n*   **%ia%**: Output the intermediate address of this instruction or pointer if applicable. Forced length of 6.\n*   **%pc%**: Output the SNES address of this code. Forced length of 6.\n*   **%offset%**: Output the ROM file offset of this code. Forced length of -6.\n*   **%bytes%**: Output the bytecode of this instruction. Forced length of 8.\n*   **%comment:length%**: Output the comment for this line.\n*   **%b%**: Output the Data Bank register for this line. Forced length of 2.\n*   **%d%**: Output the Direct Page register for this line. Forced length of 4.\n*   **%m:length%**: Output the M flag for this line. Default length of 1. If length is 1, it will output \"M\" when set and \"m\" when cleared. Otherwise, it will output \"08\" or \"16\".\n*   **%x:length%**: Output the X flag for this line. Default length of 1. If length is 1, it will output \"X\" when set and \"x\" when cleared. Otherwise, it will output \"08\" or \"16\".\n\nYou can also check the box for generating all labels. This will print a labels.csv and all-labels.txt which will show all labels regardless\nof whether they are used in the final assembly (handy if your labels are for memory addresses that are not directly referenced in the code)\n\n* * *\n\n### Unlabeled Instructions\n\nThis controls how DiztinGUIsh will output lines that have no labels.\n\n*   **Create All**: Temporary labels will be added to every line.\n*   **In Points Only**: Temporary labels will only be added to in points and read points.\n*   **None**: No extra labels will be added.\n\n* * *\n\n### Bank Structure\n\nThis controls the structure of the output disassembly.\n\n*   **All in one file**: The disassembly will be contained within one, potentially large file.\n*   **One bank per file**: Each bank will be put in a separate bank\\_\\*.asm file. A file called main.asm will be created as the root file, \n     and a file called labels.asm will be created to store all label assignments (e.g. RAM addresses).\n\n* * *\n\n### Max Data Bytes Per Line\n\nThis controls the maximum number of bytes output per line on bytes marked as data.\n\nThis is actually not the max, but the \"min-max\"--a maximum of 5 bytes will still allow two 32-bit data elements (total of 8 bytes) per line.\nA maximum of 4 bytes will limit this to one 32-bit data element (4 bytes). In other words, a data element will not be split over two lines.\n\n* * *\n\n### Reassembly with Asar\n\nIf DiztinGUIsh outputs a disassembly without any warnings, it should _always_ be assemblable with Asar. \nThe code may be desynched or flagged incorrectly, but it should still assemble at least. You can assemble a \ndisassembly via the command asar.exe \\[name\\].asm \\[output\\].sfc, where \\[name\\] is the disassembly file\n(or main.asm if output to multiple files).\n\nIn theory if the world were perfect, the reassembled ROM should match the original ROM. Sometimes it will, \nand sometimes it won't. It depends on the code itself. The output should always _run_ similarly \n(unless the ROM has built-in modification or checksum detection).\n\nThe reason this may not be the case is due to mirroring, and the way DiztinGUIsh deals with labels. \nTake the following two code snippets: JML $C00054 : JML $400054. One has an intermediate address of $C00054 \nand the other has intermediate address $400054. These two addresses refer to the same byte in the ROM due to\nmemory mapping mirroring. That is, they both refer to PC offset 0x54. DiztinGUIsh stores labels according to SNES addresses.\n\n* * *\n\nProject File Format\n-------------------\n\nThe default file format is .diz, which is XML compressed with gzip. (you can rename the file to .xml.gz)\n\nYou can also save with a .dizraw extension, which is the same thing but without the gzip the compression. It still has \na little bit of run-length and other compression.\n\nChoose this option if you want to easily modify project files in an external program, or for use with git where\nyou want to see the diffs.  The underlying format is XML.\n\nIn short, what files really are:\n*.diz files are really .xml.gz files\n*.dizraw files are really just .xml files\n\n* * *\n\n16Bit Pointers and Banks\n---------------------------------\nBy default, data marked as 16bit pointers will use the current bank as the bank for the IA.\n\nIf you set the bank on these 16bit pointer rows to a non-zero value, Diz will use that as the override\n\n(Experimental) Comment Special Directives\n---------------------------------\n\nSpecial directives can be added to comments to alter label generation. All commands start with \"!!\". You can still put a semicolon in to make a comment.\n\nUSE AT YOUR OWN RISK. THESE HAVE MINIMAL CHECKING OR VALIDATION\n\n```\n!!n\n```\n- Don't generate labels for this line or the destination IA\n- This is great if Diz is making up labels that aren't appropriate for this line (like PEA addresses, or for sections of code that modify the D register to load parts of the game dynamically) \n\nExample: override the current label for this line and paste the following text in:\n```\n!!o [whatever you want to show up there] ; [optional comment]\n```\n\nYou can use structs, labels, math, constants, anything you want.  Example: Say you have a line like:\n\n```asm\nLDA.W #$0B,X\n```\n\ntype in:\n\n```\n!!o !num_in_party\n```\n\nand the new line will be:\n\n```asm\nLDA.W #!num_in_party,X\n```\n\nIf you use this with an string that looks like an asar define (i.e. starts with an exclamation point, like \"!num_players\", \"!max_health\", \"!max_height\", etc), then Diz will also automatically create a label for you in defines.asm and assign it to $0B and create a define for you in defines.asm.\n\n-----\n\nor let's say you have:\n\n```asm\nLDA.W some_label,X\n```\n\nthen you can override with:\n\n```\n!!o some_address+some_other_address\n```\n\n```asm\nLDA.W some_address+some_other_address,X\n```\n\nThis lets you annotate constants as you go and re-use them through the project.\n\n----\n\n```!!is``` and - ```!!ie```\n\n\"include start\" and \"include end\".\n\ndefine a label on a line and use the !!is directive to dynamically-defined a region that will generate an \"incsrc\" directive.\nfurther down, use the !!ir directive to mark the end of a dynamic region.\n\nexample:\n\n```asm\nSTZ $00 ; whatever here, then\n\nsome_graphics:      !!is\n...\n...\n...\n                    !!ie\n                    \nSTZ $00 ; whatever after\n```\n\nthis will generate output like this, with the contents between start and end being in a new file that's included:\n\n```asm\nSTZ $00\nincsrc \"some_graphics.asm\"\nSTZ $00\n```\n"
  },
  {
    "path": "Diz.App.Winforms/dist/docs/Library Licenses.txt",
    "content": "Castle.Core\thttp://www.apache.org/licenses/LICENSE-2.0.html\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.abstractions\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.analyzers\thttps://raw.githubusercontent.com/xunit/xunit.analyzers/master/LICENSE\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.assert\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.core\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.extensibility.core\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.extensibility.execution\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.runner.console\thttps://raw.githubusercontent.com/xunit/xunit/master/license.txt\tApache 2.0\t\t\t\t\t\t\t\t\t\t\t\t\nMoq\thttps://raw.githubusercontent.com/moq/moq4/master/License.txt\tBSD 3clause\t\t\t\t\t\t\t\t\t\t\t\t\nByteSize\thttps://github.com/omar/ByteSize/blob/master/LICENSE\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nExtendedXmlSerializer\thttps://github.com/ExtendedXmlSerializer/home/blob/master/LICENSE\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nFastBitmapLib\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nGitInfo\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.Abstractions\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.Abstractions.Collections\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.Abstractions.Threading\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.Observable\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.StandardExtensions\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.StandardExtensions.ComponentModel\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.StandardExtensions.Threading\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nIX.Undoable\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nJetBrains.Annotations\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nLightInject\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nLiveCharts\thttps://github.com/beto-rodriguez/Live-Charts/blob/master/LICENSE.TXT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nNReco.LambdaParser\thttps://raw.githubusercontent.com/nreco/lambdaparser/master/LICENSE\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nSharpZipLib\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nSprache\thttps://github.com/sprache/Sprache/blob/master/licence.txt\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\nxunit.runner.visualstudio\thttps://licenses.nuget.org/MIT\tMIT\t\t\t\t\t\t\t\t\t\t\t\t\n--------------------------------------------------\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\"MOQ\nBSD 3-Clause License\n\nCopyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD,\nand Contributors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice,\n    this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in the\n    documentation and/or other materials provided with the distribution.\n\n    * Neither the names of the copyright holders nor the names of its\n    contributors may be used to endorse or promote products derived from this\n    software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"\"AS IS\"\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n--------------------------------------------------\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n"
  },
  {
    "path": "Diz.App.Winforms/dist/docs/hardware_registers.csv",
    "content": "002100,SNES_INIDISP\n002101,SNES_OBJSEL\n002102,SNES_OAMADDL\n002103,SNES_OAMADDH\n002104,SNES_OAMDATA\n002105,SNES_BGMODE\n002106,SNES_MOSAIC\n002107,SNES_BG1SC\n002108,SNES_BG2SC\n002109,SNES_BG3SC\n00210A,SNES_BG4SC\n00210B,SNES_BG12NBA\n00210C,SNES_BG34NBA\n00210D,SNES_BG1HOFS\n00210E,SNES_BG1VOFS\n00210F,SNES_BG2HOFS\n002110,SNES_BG2VOFS\n002111,SNES_BG3HOFS\n002112,SNES_BG3VOFS\n002113,SNES_BG4HOFS\n002114,SNES_BG4VOFS\n002115,SNES_VMAINC\n002116,SNES_VMADDL\n002117,SNES_VMADDH\n002118,SNES_VMDATAL\n002119,SNES_VMDATAH\n00211A,SNES_M7SEL\n00211B,SNES_M7A\n00211C,SNES_M7B\n00211D,SNES_M7C\n00211E,SNES_M7D\n00211F,SNES_M7X\n002120,SNES_M7Y\n002121,SNES_CGADD\n002122,SNES_CGDATA\n002123,SNES_W12SEL\n002124,SNES_W34SEL\n002125,SNES_WOBJSEL\n002126,SNES_WH0\n002127,SNES_WH1\n002128,SNES_WH2\n002129,SNES_WH3\n00212A,SNES_WBGLOG\n00212B,SNES_WOBJLOG\n00212C,SNES_TM\n00212D,SNES_TS\n00212E,SNES_TMW\n00212F,SNES_TSW\n002130,SNES_CGSWSEL\n002131,SNES_CGADSUB\n002132,SNES_COLDATA\n002133,SNES_SETINI\n002134,SNES_MPYL\n002135,SNES_MPYM\n002136,SNES_MPYH\n002137,SNES_SLHV\n002138,SNES_ROAMDATA\n002139,SNES_RVMDATAL\n00213A,SNES_RVMDATAH\n00213B,SNES_RCGDATA\n00213C,SNES_OPHCT\n00213D,SNES_OPVCT\n00213E,SNES_STAT77\n00213F,SNES_STAT78\n002140,SNES_APUIO0\n002141,SNES_APUIO1\n002142,SNES_APUIO2\n002143,SNES_APUIO3\n002180,SNES_WMDATA\n002181,SNES_WMADDL\n002182,SNES_WMADDM\n002183,SNES_WMADDH\n004016,SNES_JOY1\n004017,SNES_JOY2\n004200,SNES_NMITIMEN\n004201,SNES_WRIO\n004202,SNES_WRMPYA\n004203,SNES_WRMPYB\n004204,SNES_WRDIVL\n004205,SNES_WRDIVH\n004206,SNES_WRDIVB\n004207,SNES_HTIMEL\n004208,SNES_HTIMEH\n004209,SNES_VTIMEL\n00420A,SNES_VTIMEH\n00420B,SNES_MDMAEN\n00420C,SNES_HDMAEN\n00420D,SNES_MEMSEL\n004210,SNES_RDNMI\n004211,SNES_TIMEUP\n004212,SNES_HVBJOY\n004213,SNES_RDIO\n004214,SNES_RDDIVL\n004215,SNES_RDDIVH\n004216,SNES_RDMPYL\n004217,SNES_RDMPYH\n004218,SNES_CNTRL1L\n004219,SNES_CNTRL1H\n00421A,SNES_CNTRL2L\n00421B,SNES_CNTRL2H\n00421C,SNES_CNTRL3L\n00421D,SNES_CNTRL3H\n00421E,SNES_CNTRL4L\n00421F,SNES_CNTRL4H\n004300,SNES_DMA0PARAM\n004301,SNES_DMA0REG\n004302,SNES_DMA0ADDRL\n004303,SNES_DMA0ADDRM\n004304,SNES_DMA0ADDRH\n004305,SNES_DMA0CNTL\n004306,SNES_DMA0CNTH\n004307,SNES_HDMA0BANK\n004308,SNES_DMA0IDXL\n004309,SNES_DMA0IDXH\n00430A,SNES_HDMA0LINES\n004310,SNES_DMA1PARAM\n004311,SNES_DMA1REG\n004312,SNES_DMA1ADDRL\n004313,SNES_DMA1ADDRM\n004314,SNES_DMA1ADDRH\n004315,SNES_DMA1CNTL\n004316,SNES_DMA1CNTH\n004317,SNES_HDMA1BANK\n004318,SNES_DMA1IDXL\n004319,SNES_DMA1IDXH\n00431A,SNES_HDMA1LINES\n004320,SNES_DMA2PARAM\n004321,SNES_DMA2REG\n004322,SNES_DMA2ADDRL\n004323,SNES_DMA2ADDRM\n004324,SNES_DMA2ADDRH\n004325,SNES_DMA2CNTL\n004326,SNES_DMA2CNTH\n004327,SNES_HDMA2BANK\n004328,SNES_DMA2IDXL\n004329,SNES_DMA2IDXH\n00432A,SNES_HDMA2LINES\n004330,SNES_DMA3PARAM\n004331,SNES_DMA3REG\n004332,SNES_DMA3ADDRL\n004333,SNES_DMA3ADDRM\n004334,SNES_DMA3ADDRH\n004335,SNES_DMA3CNTL\n004336,SNES_DMA3CNTH\n004337,SNES_HDMA3BANK\n004338,SNES_DMA3IDXL\n004339,SNES_DMA3IDXH\n00433A,SNES_HDMA3LINES\n004340,SNES_DMA4PARAM\n004341,SNES_DMA4REG\n004342,SNES_DMA4ADDRL\n004343,SNES_DMA4ADDRM\n004344,SNES_DMA4ADDRH\n004345,SNES_DMA4CNTL\n004346,SNES_DMA4CNTH\n004347,SNES_HDMA4BANK\n004348,SNES_DMA4IDXL\n004349,SNES_DMA4IDXH\n00434A,SNES_HDMA4LINES\n004350,SNES_DMA5PARAM\n004351,SNES_DMA5REG\n004352,SNES_DMA5ADDRL\n004353,SNES_DMA5ADDRM\n004354,SNES_DMA5ADDRH\n004355,SNES_DMA5CNTL\n004356,SNES_DMA5CNTH\n004357,SNES_HDMA5BANK\n004358,SNES_DMA5IDXL\n004359,SNES_DMA5IDXH\n00435A,SNES_HDMA5LINES\n004360,SNES_DMA6PARAM\n004361,SNES_DMA6REG\n004362,SNES_DMA6ADDRL\n004363,SNES_DMA6ADDRM\n004364,SNES_DMA6ADDRH\n004365,SNES_DMA6CNTL\n004366,SNES_DMA6CNTH\n004367,SNES_HDMA6BANK\n004368,SNES_DMA6IDXL\n004369,SNES_DMA6IDXH\n00436A,SNES_HDMA6LINES\n004370,SNES_DMA7PARAM\n004371,SNES_DMA7REG\n004372,SNES_DMA7ADDRL\n004373,SNES_DMA7ADDRM\n004374,SNES_DMA7ADDRH\n004375,SNES_DMA7CNTL\n004376,SNES_DMA7CNTH\n004377,SNES_HDMA7BANK\n004378,SNES_DMA7IDXL\n004379,SNES_DMA7IDXH\n00437A,SNES_HDMA7LINES"
  },
  {
    "path": "Diz.App.Winforms/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0-windows7.0\": {\n      \"GitInfo\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.3.3, )\",\n        \"resolved\": \"3.3.3\",\n        \"contentHash\": \"l7m4M/figjWz4B3eIL2Qy3UJiP2vvTOTeavlCT/GGH0khg0uxLanh/cwTZ27AZNhim56q1Mk0nbyRXtLqF4vmg==\",\n        \"dependencies\": {\n          \"ThisAssembly.Constants\": \"1.4.1\"\n        }\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2023.3.0, )\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"LightInject\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.6.4, )\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"ByteSize\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.2\",\n        \"contentHash\": \"dSiH9B0apNU5abV3OVUs3eTfHiidEAa8IMOLGW0eEl5tB8Ba1ngkGC7RT/RGy61ifGcPzCe7DMP78KB8pA5kNg==\"\n      },\n      \"CsvHelper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"30.0.3\",\n        \"contentHash\": \"OK4xEoPKFZYZ2Km+ZTGRxuLo33u+ipKVCC9ICCGn5JRiAmPLRsCPZ/shL3HKGPwS33MQSPcPHigI4HnX3Q47yg==\"\n      },\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FastBitmapLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.0\",\n        \"contentHash\": \"FOk+mBCg8nenI3V4kJXK/QeHKKjbPTZdebx8CGoMHX9bO4wzlnTSasYCHtl29FYByn+AInl9Dj1ICt29wWaL0w==\",\n        \"dependencies\": {\n          \"System.Drawing.Common\": \"4.5.1\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.5.2\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"VdLJOCXhZaEMY7Hm2GKiULmn7IEPFE4XC5LPSfBVCUIA8YLZVh846gtfBJalsPQF2PlzdD7ecX7DZEulJ402ZQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.SystemEvents\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Drawing.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.1\",\n        \"contentHash\": \"GiyeGi/v4xYDz1vCNFwFvhz9k1XddOG7VD3jxRqzRBCbTHji+s3HxxbxtoymuK4OadEpgotI8zQ5+GEEH9sUEQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.0.0\",\n          \"Microsoft.Win32.SystemEvents\": \"4.5.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.2\",\n        \"contentHash\": \"wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.4\",\n        \"contentHash\": \"zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==\"\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"ThisAssembly.Constants\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.1\",\n        \"contentHash\": \"sV0CDYlC6YSIkYbdwd4+jRinLb888Dac71pmNhF2Ll6UXY1J+1oVt3EfmX6euXEKz2RLdfV+4GyZctfEA0NSqA==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.7.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.4\"\n        }\n      },\n      \"diz.app.common\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Controllers\": \"[1.0.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      },\n      \"diz.controllers\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Import\": \"[1.0.0, )\",\n          \"Diz.LogWriter\": \"[1.0.0, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"FluentValidation\": \"[11.9.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"JetBrains.Profiler.SelfApi\": \"[2.5.0, )\",\n          \"LightInject\": \"[6.6.4, )\",\n          \"SharpZipLib\": \"[1.4.2, )\",\n          \"System.Diagnostics.Tracing\": \"[4.3.0, )\",\n          \"System.Resources.Extensions\": \"[8.0.0, )\",\n          \"System.Text.Encoding.CodePages\": \"[8.0.0, )\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      },\n      \"diz.cpu.65816\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.import\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.logwriter\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CsvHelper\": \"[30.0.3, )\",\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      },\n      \"diz.ui.winforms\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"ByteSize\": \"[2.1.2, )\",\n          \"Diz.Controllers\": \"[1.0.0, )\",\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"FastBitmapLib\": \"[2.1.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      }\n    },\n    \"net9.0-windows7.0/win-x64\": {\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.win.Microsoft.Win32.Primitives\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.SystemEvents\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.0.0\"\n        }\n      },\n      \"runtime.any.System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"runtime.any.System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"S/GPBmfPBB48ZghLxdDR7kDAJVAqgAuThyDJho3OLP5OS4tWD2ydyL8LKm8lhiBxce10OKe9X2zZ6DUjAqEbPg==\"\n      },\n      \"runtime.any.System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1lpifymjGDzoYIaam6/Hyqf8GhBI3xXYLK2TgEvTtuZMorG3Kb9QnMTIKhLjJYXIiu1JvxjngHvtVFQQlpQ3HQ==\"\n      },\n      \"runtime.any.System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==\"\n      },\n      \"runtime.any.System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"M1r+760j1CNA6M/ZaW6KX8gOS8nxPRqloqDcJYVidRG566Ykwcs29AweZs2JF+nMOCgWDiMfPSTMfvwOI9F77w==\"\n      },\n      \"runtime.any.System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==\"\n      },\n      \"runtime.any.System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==\"\n      },\n      \"runtime.any.System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==\"\n      },\n      \"runtime.any.System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==\"\n      },\n      \"runtime.any.System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==\"\n      },\n      \"runtime.any.System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==\",\n        \"dependencies\": {\n          \"System.Private.Uri\": \"4.3.0\"\n        }\n      },\n      \"runtime.any.System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==\"\n      },\n      \"runtime.any.System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==\"\n      },\n      \"runtime.any.System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==\"\n      },\n      \"runtime.any.System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NLrxmLsfRrOuVqPWG+2lrQZnE53MLVeo+w9c54EV+TUo4c8rILpsDXfY8pPiOy9kHpUHHP07ugKmtsU3vVW5Jg==\"\n      },\n      \"runtime.any.System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==\"\n      },\n      \"runtime.any.System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"w4ehZJ+AwXYmGwYu+rMvym6RvMaRiUEQR1u6dwcyuKHxz8Heu/mO9AG1MquEgTyucnhv3M43X0iKpDOoN17C0w==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"runtime.win.Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NU51SEt/ZaD2MF48sJ17BIqx7rjeNNLXUevfMOjqQIetdndXwYjZfZsT6jD+rSWp/FYxjesdK4xUSl4OTEI0jw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"vHPXC3B18dxhyipVce8xQT1MQv1o5srYZqBlCNu9p9MNjhgGOntdQh/Xh2X4o7M2F839YUcQiGwu8Q498FyDjg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"hHHP0WCStene2jjeYcuDkETozUYF/3sHVRHAEOgS3L15hlip24ssqCTnJC28Z03Wpo078oMcJd0H4egD2aJI8g==\"\n      },\n      \"runtime.win.System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z37zcSCpXuGCYtFbqYO0TwOVXxS2d+BXgSoDFZmRg8BC4Cuy54edjyIvhhcfCrDQA9nl+EPFTgHN54dRAK7mNA==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Overlapped\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lkXXykakvXUU+Zq2j0pC6EO20lEhijjqMc01XXpp1CJN+DeCwl3nsj4t5Xbpz3kA7yQyTqw6d9SyIzsyLsV3zA==\",\n        \"dependencies\": {\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FK/2gX6MmuLIKNCGsV59Fe4IYrLrI5n9pQ1jh477wiivEM/NCXDT2dRetH5FSfY0bQ+VgTLcS3zcmjQ8my3nxQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Net.NameResolution\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Overlapped\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RkgHVhUPvzZxuUubiZe8yr/6CypRVXj0VBzaR8hsqQ8f+rUo7e4PWrHTLOCjd8fBMGWCrY//fi7Ku3qXD7oHRw==\",\n        \"dependencies\": {\n          \"System.Private.Uri\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Collections\": \"4.3.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.win.System.Console\": \"4.3.1\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.win.System.Diagnostics.Debug\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Diagnostics.Tools\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Diagnostics.Tracing\": \"4.3.0\"\n        }\n      },\n      \"System.Drawing.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.1\",\n        \"contentHash\": \"GiyeGi/v4xYDz1vCNFwFvhz9k1XddOG7VD3jxRqzRBCbTHji+s3HxxbxtoymuK4OadEpgotI8zQ5+GEEH9sUEQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.0.0\",\n          \"Microsoft.Win32.SystemEvents\": \"4.5.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Globalization\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Globalization.Calendars\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.any.System.IO\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.win.System.IO.FileSystem\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.NameResolution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"AFYl08R7MrsrEjqpQWTZWBadqXyTzNDaWpMqyxhb0d6sGhV6xMDKueuBXlLL30gz+DIRY6MpdgnHWlCh5wmq9w==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"runtime.win.System.Net.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.win.System.Net.Sockets\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Reflection\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Reflection.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Reflection.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Resources.ResourceManager\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"runtime.any.System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.win.System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"runtime.any.System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Claims\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HVL1rvqYtnRCxFsYag/2le/ZfKLK4yMw79+s6FmKXbSCNN0JeAhrYxnRAHFoWRa0dEojsDcbBSpH3l22QxAVyw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Claims\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.any.System.Text.Encoding.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Overlapped\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m3HQ2dPiX/DSTpf+yJt8B0c+SRvzfqAJKx+QDWi+VLhz8svLT23MVjEOHPF/KiSLeArKU/iHescrbLd3yVgyNg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Threading.Timer\": \"4.3.0\"\n        }\n      }\n    },\n    \"net9.0-windows7.0/win-x86\": {\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.win.Microsoft.Win32.Primitives\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.SystemEvents\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.0.0\"\n        }\n      },\n      \"runtime.any.System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"runtime.any.System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"S/GPBmfPBB48ZghLxdDR7kDAJVAqgAuThyDJho3OLP5OS4tWD2ydyL8LKm8lhiBxce10OKe9X2zZ6DUjAqEbPg==\"\n      },\n      \"runtime.any.System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1lpifymjGDzoYIaam6/Hyqf8GhBI3xXYLK2TgEvTtuZMorG3Kb9QnMTIKhLjJYXIiu1JvxjngHvtVFQQlpQ3HQ==\"\n      },\n      \"runtime.any.System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==\"\n      },\n      \"runtime.any.System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"M1r+760j1CNA6M/ZaW6KX8gOS8nxPRqloqDcJYVidRG566Ykwcs29AweZs2JF+nMOCgWDiMfPSTMfvwOI9F77w==\"\n      },\n      \"runtime.any.System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==\"\n      },\n      \"runtime.any.System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==\"\n      },\n      \"runtime.any.System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==\"\n      },\n      \"runtime.any.System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==\"\n      },\n      \"runtime.any.System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==\"\n      },\n      \"runtime.any.System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==\",\n        \"dependencies\": {\n          \"System.Private.Uri\": \"4.3.0\"\n        }\n      },\n      \"runtime.any.System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==\"\n      },\n      \"runtime.any.System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==\"\n      },\n      \"runtime.any.System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==\"\n      },\n      \"runtime.any.System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NLrxmLsfRrOuVqPWG+2lrQZnE53MLVeo+w9c54EV+TUo4c8rILpsDXfY8pPiOy9kHpUHHP07ugKmtsU3vVW5Jg==\"\n      },\n      \"runtime.any.System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==\"\n      },\n      \"runtime.any.System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"w4ehZJ+AwXYmGwYu+rMvym6RvMaRiUEQR1u6dwcyuKHxz8Heu/mO9AG1MquEgTyucnhv3M43X0iKpDOoN17C0w==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"runtime.win.Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NU51SEt/ZaD2MF48sJ17BIqx7rjeNNLXUevfMOjqQIetdndXwYjZfZsT6jD+rSWp/FYxjesdK4xUSl4OTEI0jw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"vHPXC3B18dxhyipVce8xQT1MQv1o5srYZqBlCNu9p9MNjhgGOntdQh/Xh2X4o7M2F839YUcQiGwu8Q498FyDjg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"hHHP0WCStene2jjeYcuDkETozUYF/3sHVRHAEOgS3L15hlip24ssqCTnJC28Z03Wpo078oMcJd0H4egD2aJI8g==\"\n      },\n      \"runtime.win.System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z37zcSCpXuGCYtFbqYO0TwOVXxS2d+BXgSoDFZmRg8BC4Cuy54edjyIvhhcfCrDQA9nl+EPFTgHN54dRAK7mNA==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Overlapped\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lkXXykakvXUU+Zq2j0pC6EO20lEhijjqMc01XXpp1CJN+DeCwl3nsj4t5Xbpz3kA7yQyTqw6d9SyIzsyLsV3zA==\",\n        \"dependencies\": {\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FK/2gX6MmuLIKNCGsV59Fe4IYrLrI5n9pQ1jh477wiivEM/NCXDT2dRetH5FSfY0bQ+VgTLcS3zcmjQ8my3nxQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Net.NameResolution\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Overlapped\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"runtime.win.System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RkgHVhUPvzZxuUubiZe8yr/6CypRVXj0VBzaR8hsqQ8f+rUo7e4PWrHTLOCjd8fBMGWCrY//fi7Ku3qXD7oHRw==\",\n        \"dependencies\": {\n          \"System.Private.Uri\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Collections\": \"4.3.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.win.System.Console\": \"4.3.1\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.win.System.Diagnostics.Debug\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Diagnostics.Tools\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Diagnostics.Tracing\": \"4.3.0\"\n        }\n      },\n      \"System.Drawing.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.1\",\n        \"contentHash\": \"GiyeGi/v4xYDz1vCNFwFvhz9k1XddOG7VD3jxRqzRBCbTHji+s3HxxbxtoymuK4OadEpgotI8zQ5+GEEH9sUEQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"2.0.0\",\n          \"Microsoft.Win32.SystemEvents\": \"4.5.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Globalization\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Globalization.Calendars\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.any.System.IO\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.win.System.IO.FileSystem\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.NameResolution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"AFYl08R7MrsrEjqpQWTZWBadqXyTzNDaWpMqyxhb0d6sGhV6xMDKueuBXlLL30gz+DIRY6MpdgnHWlCh5wmq9w==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"runtime.win.System.Net.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.win.System.Net.Sockets\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Reflection\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Reflection.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Reflection.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Resources.ResourceManager\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"runtime.any.System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.win.System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"runtime.any.System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Claims\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HVL1rvqYtnRCxFsYag/2le/ZfKLK4yMw79+s6FmKXbSCNN0JeAhrYxnRAHFoWRa0dEojsDcbBSpH3l22QxAVyw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Claims\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.any.System.Text.Encoding.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Overlapped\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m3HQ2dPiX/DSTpf+yJt8B0c+SRvzfqAJKx+QDWi+VLhz8svLT23MVjEOHPF/KiSLeArKU/iHescrbLd3yVgyNg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"runtime.any.System.Threading.Timer\": \"4.3.0\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/Diz.Controllers.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n        <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n        <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\..\\Diz.Core\\Diz.Core.csproj\" />\n      <ProjectReference Include=\"..\\..\\Diz.Import\\Diz.Import.csproj\" />\n      <ProjectReference Include=\"..\\..\\Diz.LogWriter\\Diz.LogWriter.csproj\" />\n    </ItemGroup>\n\n    <ItemGroup>\n      <PackageReference Include=\"LightInject\">\n        <Version>6.6.4</Version>\n      </PackageReference>\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {\n      \"LightInject\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.6.4, )\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"CsvHelper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"30.0.3\",\n        \"contentHash\": \"OK4xEoPKFZYZ2Km+ZTGRxuLo33u+ipKVCC9ICCGn5JRiAmPLRsCPZ/shL3HKGPwS33MQSPcPHigI4HnX3Q47yg==\"\n      },\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"FluentValidation\": \"[11.9.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"JetBrains.Profiler.SelfApi\": \"[2.5.0, )\",\n          \"LightInject\": \"[6.6.4, )\",\n          \"SharpZipLib\": \"[1.4.2, )\",\n          \"System.Diagnostics.Tracing\": \"[4.3.0, )\",\n          \"System.Resources.Extensions\": \"[8.0.0, )\",\n          \"System.Text.Encoding.CodePages\": \"[8.0.0, )\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      },\n      \"diz.cpu.65816\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.import\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.logwriter\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CsvHelper\": \"[30.0.3, )\",\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/AssemblyInfo.cs",
    "content": "﻿using Diz.Controllers.services;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizControllersCompositionRoot))]"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/IImportRomDialogController.cs",
    "content": "﻿using Diz.Controllers.interfaces;\nusing Diz.Core.serialization;\nusing Diz.Cpu._65816.import;\n\nnamespace Diz.Controllers.controllers;\n\npublic interface IImportRomDialogController\n{\n    IImportRomDialogView View { get; set; }\n    public ISnesRomImportSettingsBuilder Builder { get; }\n    public event SettingsCreatedEvent OnBuilderInitialized;\n        \n    string CartridgeTitle { get; }\n    string RomSpeedText { get; }\n\n    ImportRomSettings PromptUserForImportOptions(string romFilename);\n        \n    delegate void SettingsCreatedEvent();\n\n    bool Submit();\n    public int ReadRomVectorTableEntryValueWord(int vectorEntryTableStartOffset);\n    bool IsProbablyValidDetection();\n    string GetDetectionMessage();\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/IProjectView.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.model;\nusing Diz.LogWriter;\n\nnamespace Diz.Controllers.controllers\n{\n    public interface ILongRunningTaskHandler\n    {\n        public delegate void LongRunningTaskHandler(Action task, string description, IProgressView progressView);\n        LongRunningTaskHandler TaskHandler { get; }\n    }\n    \n    public interface IMainGridWindowView : IProjectView, IFormViewer\n    {\n    \n    }\n    \n    public interface IProjectView : ILongRunningTaskHandler, ISnesNavigation\n    {\n        Project Project { get; set; }\n        void OnProjectOpenFail(string errorMsg);\n        void OnProjectSaved();\n        void OnExportFinished(LogCreatorOutput.OutputResult result);\n        \n        string AskToSelectNewRomFilename(string promptSubject, string promptText);\n        void OnProjectOpenWarnings(IEnumerable<string> warnings);\n    }\n\n    public interface ISnesNavigation\n    {\n        public class HistoryArgs\n        {\n            public string Description { get; set; }\n            public string Position { get; set; }\n        }\n        \n        /// <summary>\n        /// Select a PC offset\n        /// </summary>\n        /// <param name=\"pcOffset\">PC [not SNES] offset</param>\n        /// <param name=\"historyArgs\">if non-null, record this event in the project history</param>\n        void SelectOffset(int pcOffset, HistoryArgs historyArgs = null);\n        void SelectOffsetWithOvershoot(int pcOffset, int overshootAmount = 0);\n        \n        // get the PC offset of the currently selected row in the view\n        public int SelectedOffset { get; }\n    }\n}\n"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/ImportROMDialogController.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.Interfaces;\nusing Diz.Core.serialization;\nusing Diz.Core.util;\nusing Diz.Cpu._65816;\nusing Diz.Cpu._65816.import;\nusing JetBrains.Annotations;\n\nnamespace Diz.Controllers.controllers;\n\n[SuppressMessage(\"ReSharper\", \"ClassNeverInstantiated.Global\")]\npublic class ImportRomDialogController : IImportRomDialogController\n{\n    public IImportRomDialogView View { get; set; }\n    public ISnesRomImportSettingsBuilder Builder { get; }\n        \n    public event IImportRomDialogController.SettingsCreatedEvent OnBuilderInitialized;\n        \n    private readonly ICommonGui commonGui;\n\n    public int RomSettingsOffset\n    {\n        get\n        {\n            if (Builder.Input.AnalysisResults == null)\n                return -1;\n                \n            return RomUtil.GetRomSettingOffset(Builder.OptionSelectedRomMapMode);\n        }\n    }\n\n    public IReadOnlyList<byte> RomBytes => \n        Builder.Input.RomBytes;\n\n    public RomSpeed RomSpeed =>\n        Builder.Input.AnalysisResults?.RomSpeed ?? RomSpeed.Unknown;\n\n    public string CartridgeTitle => \n        RomUtil.GetCartridgeTitleFromRom(RomBytes, RomSettingsOffset);\n\n    public string RomSpeedText => \n        Util.GetEnumDescription(RomSpeed);\n        \n    public string GetDetectionMessage()\n    {\n        string msg = null;\n        if (Builder.Input.AnalysisResults is { DetectedRomMapModeCorrectly: true })\n            msg = RomMapModeText;\n        \n        return msg ?? \"Couldn't auto detect ROM Map Mode!\";\n    }\n\n    [CanBeNull]\n    public string RomMapModeText\n    {\n        get\n        {\n            var romMapModeTxt = Builder?.Input?.AnalysisResults?.RomMapMode;\n            return romMapModeTxt != null ? Util.GetEnumDescription(romMapModeTxt) : null;\n        }\n    }\n\n    public ImportRomDialogController(ICommonGui commonGui, IImportRomDialogView view, ISnesRomImportSettingsBuilder builder)\n    {\n        this.commonGui = commonGui;\n        Builder = builder;\n            \n        View = view;\n    }\n\n    public ImportRomSettings PromptUserForImportOptions(string romFilename)\n    {\n        return !PromptUserForOptions(romFilename)\n            ? null \n            : Builder.GenerateSettings();\n    }\n\n    private string romFileNameAnalyzed = \"\";\n\n    private bool PromptUserForOptions(string romFilename)\n    {\n        Debug.Assert(Builder != null);\n     \n        romFileNameAnalyzed = romFilename;\n        ReAnalyze();\n            \n        Builder.PropertyChanged += BuilderOnPropertyChanged;\n        OnBuilderInitialized?.Invoke();\n\n        Refresh();\n        var result = View.ShowAndWaitForUserToConfirmSettings();\n        Refresh();\n            \n        Builder.PropertyChanged -= BuilderOnPropertyChanged;\n        View = null;\n            \n        return result;\n    }\n\n    private void ReAnalyze()\n    {\n        Builder.Analyze(romFileNameAnalyzed);\n    }\n\n    private void BuilderOnPropertyChanged(object sender, PropertyChangedEventArgs e)\n    {\n        Refresh();\n    }\n\n    private bool IsOffsetInRange(int offset) => \n        Builder.Input.RomBytes != null && offset > 0 && offset <= Builder.Input.RomBytes.Count;\n\n    public bool IsProbablyValidDetection() =>\n        Builder.Input.AnalysisResults != null &&\n        Builder.Input.AnalysisResults.RomSpeed != RomSpeed.Unknown &&\n        IsOffsetInRange(Builder.Input.RomSettingsOffset ?? -1);\n    \n    private void Refresh()\n    {\n        View?.RefreshUi();\n        SyncVectorTableEntriesFromGui();\n    }\n    \n    private void SyncVectorTableEntriesFromGui()\n    {\n        Builder.OptionClearGenerateVectorTableLabels();\n        foreach (var vectorEntry in View.EnabledVectorTableEntries)\n        {\n            Builder.OptionSetGenerateVectorTableLabelFor(vectorEntry, true);\n        }\n    }\n\n    private bool Warn(string msg)\n    {\n        return commonGui.PromptToConfirmAction(msg +\n                                               \"\\nIf you proceed with this import, imported data might be wrong.\\n\" +\n                                               \"Proceed anyway?\\n\\n (Experts only, otherwise say No)\");\n    }\n\n    public bool Submit()\n    {\n        if (Builder == null)\n        {\n            Warn(\"Internal error (couldn't build new ROM import settings). Aborting\");\n            return false;\n        }\n\n        var analysisResults = Builder.Input.AnalysisResults;\n        if (analysisResults == null)\n        {\n            Warn(\"Internal error (Rom analysis results were empty). Aborting\");\n            return false;\n        }\n            \n        if (!analysisResults.DetectedRomMapModeCorrectly)\n        {\n            if (!Warn(\"ROM Map type couldn't be detected.\"))\n                return false;\n        }\n        else if (analysisResults.RomMapMode != Builder.OptionSelectedRomMapMode)\n        {\n            if (!Warn(\"The ROM map type selected is different than what was detected.\"))\n                return false;\n        }\n\n        return true;\n    }\n    \n    public int ReadRomVectorTableEntryValueWord(int vectorEntryTableStartOffset)\n    {\n        Debug.Assert(vectorEntryTableStartOffset % 2 == 0);\n        var romOffset = RomSettingsOffset + CpuVectorTable.VectorTableSettingsOffset + vectorEntryTableStartOffset;\n        return ReadRomWord(romOffset);\n    }\n\n    private int ReadRomWord(int romOffset) => \n        RomBytes[romOffset] + (RomBytes[romOffset + 1] << 8);\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/LogCreatorExportSettingsEditorController.cs",
    "content": "﻿#nullable enable\n\nusing System;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.CompilerServices;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.export;\nusing Diz.Core.util;\nusing Diz.LogWriter.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Controllers.controllers;\n\n[UsedImplicitly]\npublic class LogCreatorSettingsEditorController : ILogCreatorSettingsEditorController\n{\n    private enum PromptCreateDirResult\n    {\n        AlreadyExists,\n        DontWantToCreateItNow,\n        DidCreateItNow,\n    }\n    \n    public LogWriterSettings Settings\n    {\n        get => settings;\n        set => this.SetField(PropertyChanged, ref settings, value);\n    }\n\n    public string? KeepPathsRelativeToThisPath\n    {\n        get => keepPathsRelativeToThisPath;\n        set => this.SetField(PropertyChanged, ref keepPathsRelativeToThisPath, value);\n    }\n\n    public ILogCreatorSettingsEditorView View { get; set; }\n    \n    public event EventHandler? Closed;\n    public event PropertyChangedEventHandler? PropertyChanged;\n    \n    private LogWriterSettings settings = new();\n    private string? keepPathsRelativeToThisPath;\n    private readonly IFilesystemService fs;\n\n    public LogCreatorSettingsEditorController(ILogCreatorSettingsEditorView view, IFilesystemService fs, Func<LogWriterSettings, ISampleAssemblyTextGenerator> sampleAssemblyOutputFactory)\n    {\n        this.fs = fs;\n        this.sampleAssemblyOutputFactory = sampleAssemblyOutputFactory;\n        Debug.Assert(fs != null);\n        \n        View = view;\n        View.Controller = this;\n        View.OnFormClosed += OnClosed;\n    }\n    \n    /// <summary>\n    /// Show settings editor UI for user to edit.\n    /// NOTE: edited settings don't have to be valid when this returns.\n    /// </summary>\n    /// <returns>True if settings were saved, false if user cancelled the operation and we should discard edits</returns>\n    public bool PromptSetupAndValidateExportSettings() => \n        View.PromptEditAndConfirmSettings();\n    \n    [NotifyPropertyChangedInvocator]\n    public void OnPropertyChanged([CallerMemberName] string? propertyName = null) => \n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n\n    public bool EnsureSelectRealOutputDirectory(bool forcePrompt = false)\n    {\n        var result = PromptToCreateOutputDirIfNeeded(\"Press YES to create and use this path, NO to select a new path instead.\");\n\n        if (result == PromptCreateDirResult.DidCreateItNow)\n            return true;\n\n        var shouldPrompt = forcePrompt || result == PromptCreateDirResult.DontWantToCreateItNow;\n        if (shouldPrompt && !PromptForPath())\n            return false;\n        \n        return PromptToCreateOutputDirIfNeeded() is \n            PromptCreateDirResult.AlreadyExists or \n            PromptCreateDirResult.DidCreateItNow;\n    }\n\n\n    private readonly Func<LogWriterSettings, ISampleAssemblyTextGenerator> sampleAssemblyOutputFactory;\n\n    public string GetSampleOutput()\n    {\n        try\n        {\n            var sampleAssemblyTextGenerator = sampleAssemblyOutputFactory(Settings);\n            return sampleAssemblyTextGenerator.GetSampleAssemblyOutput().AssemblyOutputStr;\n        }\n        catch (Exception ex)\n        {\n            return $\"Invalid format or sample output: {ex.Message}\";\n        }\n    }\n\n    /// <summary>\n    /// If the output directory doesn't exist, ask the user if they'd like to create it. \n    /// </summary>\n    /// <returns>true if the directory exists (either already existing or was just created), false if user chose not to create it</returns>\n    private PromptCreateDirResult PromptToCreateOutputDirIfNeeded(string extraMsg = \"\")\n    {\n        if (DoesOutputDirExist())\n            return PromptCreateDirResult.AlreadyExists; // already exists, so we're good.\n\n        // doesn't exist, ask if they want to create it now\n        if (!View.PromptCreatePath(Settings.BuildFullOutputPath(), extraMsg))\n            return PromptCreateDirResult.DontWantToCreateItNow; // they don't want to create it now\n            \n        // yes, they want to create the directory now\n        CreateOutputDirIfNeeded();\n        return PromptCreateDirResult.DidCreateItNow;\n    }\n\n    private void CreateOutputDirIfNeeded()\n    {\n        if (DoesOutputDirExist())\n            return;\n\n        // TODO: catch exceptions here.\n        var outputDirectoryName = GetOutputDirectoryName();\n        if (!string.IsNullOrEmpty(outputDirectoryName)) \n            fs.CreateDirectory(outputDirectoryName);\n    }\n\n    private bool PromptForPath()\n    {\n        var askForFile = Settings.Structure == LogWriterSettings.FormatStructure.SingleFile;\n        var selectedFileOrFolderOutPath = View.PromptForLogPathFromFileOrFolderDialog(askForFile);\n\n        if (string.IsNullOrEmpty(selectedFileOrFolderOutPath))\n            return false;\n\n        Settings = Settings.WithPathRelativeTo(selectedFileOrFolderOutPath, KeepPathsRelativeToThisPath);\n\n        return true;\n    }\n    \n    private string? GetOutputDirectoryName() => \n        Path.GetDirectoryName(Settings.BuildFullOutputPath());\n\n    private bool DoesOutputDirExist() => \n        fs.DirectoryExists(GetOutputDirectoryName());\n    \n    private void OnClosed(object? sender, EventArgs eventArgs) => \n        Closed?.Invoke(sender, eventArgs);\n\n    public bool ValidateExportSettings()\n    {\n        return Settings.IsValid(fs);\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/MarkManyController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Diz.Controllers.interfaces;\nusing Diz.Core;\nusing Diz.Core.commands;\nusing Diz.Core.Interfaces;\nusing Diz.Core.util;\n\nnamespace Diz.Controllers.controllers;\n\npublic class MarkManyController<TDataSource> : IMarkManyController<TDataSource> where TDataSource \n    : IRomSize\n{\n    public IDataRange DataRange { get; }\n    public TDataSource Data { get; }\n    public IMarkManyView<TDataSource> MarkManyView { get; }\n    public int DesiredStartingCount { get; set; } = 0x10;\n\n    public MarkManyController(int offset, MarkCommand.MarkManyProperty initialProperty, TDataSource data, IMarkManyView<TDataSource> view)\n    {\n        Data = data;\n        MarkManyView = view;\n        MarkManyView.Controller = this;\n\n        DataRange = new CorrectingRange(Data.GetRomSize());\n\n        DataRange.StartIndex = offset;\n        DataRange.RangeCount = Math.Min(\n            DesiredStartingCount, \n            DataRange.MaxCount - DataRange.StartIndex\n        );\n\n        MarkManyView.Property = initialProperty;\n    }\n\n    private MarkCommand CreateCommandFromView() =>\n        new()\n        {\n            Start = DataRange.StartIndex,\n            Count = DataRange.RangeCount,\n            Value = MarkManyView.GetPropertyValue(),\n            Property = MarkManyView.Property,\n        };\n\n    public MarkCommand GetMarkCommand()\n    {\n        // attempt to set to previous values from last run, if they are compatible\n        MarkManyView.AttemptSetSettings(Settings);\n        var command = !MarkManyView.PromptDialog() ? null : CreateCommandFromView();\n        Settings = MarkManyView.SaveCurrentSettings();\n        return command;\n    }\n\n    public Dictionary<MarkCommand.MarkManyProperty, object> Settings { get; set; } = new();\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/NavigationEntry.cs",
    "content": "﻿using System.ComponentModel;\nusing System.ComponentModel.DataAnnotations;\nusing Diz.Core.model.snes;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Controllers.controllers\n{\n    public class NavigationEntry\n    {\n        [Browsable(false)]\n        public Data Data { get; }\n        \n        [Browsable(false)]\n        public int SnesOffset { get; }\n        \n        public NavigationEntry(int snesOffset, [CanBeNull] ISnesNavigation.HistoryArgs historyArgs, Data data)\n        {\n            SnesOffset = snesOffset;\n            Description = historyArgs?.Description ?? \"\";\n            Position = historyArgs?.Position ?? \"\";\n            \n            Data = data;\n        }\n\n        [DisplayName(\"SNES Offset\")]\n        [Editable(false)]\n        public string Address => Util.ToHexString6(SnesOffset);\n        \n        [Editable(false)]\n        public string Description { get; }\n        \n        [Editable(false)]\n        public string Position { get; }\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/ProgressBarWorker.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing Diz.Controllers.interfaces;\n\nnamespace Diz.Controllers.controllers\n{\n    // TODO: replace this with Task and async/await. don't use threads directly.\n    public abstract class ProgressBarWorker\n    {\n        public IProgressView View { get; set; }\n        public bool IsMarquee { get; init; }\n        public string TextOverride { get; init; }\n        \n        private bool isRunning;\n        private Thread backgroundThread;\n\n        protected ProgressBarWorker(IProgressView view)\n        {\n            View = view;\n        }\n\n        protected void UpdateProgress(int i)\n        {\n            Debug.Assert(i >= 0 && i <= 100);\n            View?.Report(i);\n        }\n\n        protected abstract void Thread_DoWork();\n\n        // call from main thread to start a long-running job\n        // \n        // shows a progress bar dialog box while the work is being performed\n        // note: we're not being super-careful about thread safety here.\n        // if main thread is blocked it should be fine, but, if other things can\n        // still happen in the background, be really careful.\n        public void Run()\n        {\n            Setup();\n            backgroundThread.Start();\n            WaitForJobToFinish();\n        }\n\n        protected virtual void Setup()\n        {\n            if (isRunning)\n                throw new InvalidOperationException(\n                    \"Progress bar already running, existing job must finish first\");\n\n            isRunning = true;\n\n            Debug.Assert(View != null);\n            \n            \n            View.IsMarquee = IsMarquee;\n            View.TextOverride = TextOverride;\n\n            // setup, but don't start, the new thread\n            backgroundThread = new Thread(Thread_Main);\n\n            // honestly, not sure about this. works around some weird Invoke() stuff\n            // this all needs to be ripped out. there's another branch with the WIP version of that.\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                backgroundThread.SetApartmentState(ApartmentState.STA);\n            }\n        }\n\n        // blocking function\n        private void WaitForJobToFinish()\n        {\n            // blocks til worker thread closes this dialog box\n            View.PromptDialog();\n        }\n\n        // called from a new worker thread\n        private void Thread_Main()\n        {\n            try\n            {\n                // BAD APPROACH. we should instead get an event\n                // I'm too lazy right now. TODO FIXME\n                // TODO: fix: in Eto we can't call .Visible from the non-UI thread \n                while (!View.IsVisible())\n                    Thread.Sleep(50);\n\n                Thread_DoWork();\n            }\n            finally\n            {\n                isRunning = false;\n                SignalJobIsDone();\n            }\n        }\n\n        private void SignalJobIsDone()\n        {\n            // unblock the main thread from ShowDialog()\n            View?.SignalJobIsDone();\n        }\n    }\n\n\n    // a version that keeps calling 'callback' until it returns -1\n    public class ProgressBarJob : ProgressBarWorker\n    {\n        // a version that calls action once and exits\n        // shows a \"marquee\" i.e. spinner\n        public static void RunAndWaitForCompletion(Action action, string overrideTxt, IProgressView progressView)\n        {\n            Debug.Assert(progressView != null);\n            \n            var j = new ProgressBarJob(progressView)\n            {\n                MaxProgress = -1,\n                Callback = () =>\n                {\n                    action();\n                    return -1;\n                },\n                IsMarquee = (long)-1 == -1,\n                TextOverride = overrideTxt,\n            };\n            \n            j.Run();\n        }\n\n        public NextAction Callback { get; set; }\n        public long MaxProgress { get; set; }\n\n        protected override void Thread_DoWork()\n        {\n            UpdateProgress(0);\n            var progress = -1L;\n            do {\n                progress = Callback();\n                UpdateProgress(progress);\n            } while (progress > 0);\n        }\n\n        private int previousProgress;\n\n        protected void UpdateProgress(long currentProgress)\n        {\n            if (MaxProgress <= 0)\n                return;\n\n            var percent = currentProgress / (float)MaxProgress;\n            var progressValue = (int)(percent * 100);\n\n            if (progressValue <= previousProgress)\n                return;\n\n            // don't do this too often, kinda slow due to thread synchronization.\n            base.UpdateProgress(progressValue);\n\n            previousProgress = progressValue;\n        }\n\n        // return > 0 to continue. return value will be used to indicate progress in range of [0 -> MaxProgress]\n        public delegate long NextAction();\n\n        public ProgressBarJob(IProgressView view) : base(view)\n        {\n        }\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/ProjectController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Linq;\nusing Diz.Controllers.interfaces;\nusing Diz.Core;\nusing Diz.Core.export;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.util;\nusing Diz.Cpu._65816;\nusing Diz.Import;\nusing Diz.Import.bizhawk;\nusing Diz.Import.bsnes.tracelog;\nusing Diz.Import.bsnes.usagemap;\nusing Diz.LogWriter;\nusing Diz.LogWriter.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Controllers.controllers;\n\n[SuppressMessage(\"ReSharper\", \"ClassNeverInstantiated.Global\")]\npublic class ProjectController(\n    ICommonGui commonGui,\n    IFilesystemService fs,\n    IControllerFactory controllerFactory,\n    Func<ImportRomSettings, IProjectFactoryFromRomImportSettings> projectImporterFactoryCreate,\n    Func<IProjectFileManager> projectFileManagerCreate,\n    Func<IProgressView> progressViewFactoryCreate)\n    : IProjectController\n{\n    public IProjectView ProjectView { get; set; }\n    public Project Project { get; private set; }\n\n    public event IProjectController.ProjectChangedEvent ProjectChanged;\n\n    // there's probably better ways to handle this.\n    // probably replace with a UI like \"start task\" and \"stop task\"\n    // so we can flip up a progress bar and remove it.\n    public void DoLongRunningTask(Action task, string description = null)\n    {\n        if (ProjectView.TaskHandler == null)\n        {\n            // fallback\n            task();\n            return;\n        }\n\n        // normal way to do it:\n        var progressBarView = progressViewFactoryCreate();\n        ProjectView.TaskHandler(task, description, progressBarView);\n    }\n\n    public bool OpenProject(string filename)\n    {\n        ProjectOpenResult projectOpenResult = null;\n        var errorMsg = \"\";\n\n        DoLongRunningTask(delegate\n        {\n            try\n            {\n                projectOpenResult = CreateProjectFileManager().Open(filename);\n            }\n            catch (AggregateException ex)\n            {\n                projectOpenResult = null;\n                errorMsg = ex.InnerExceptions.Select(e => e.Message).Aggregate((line, val) => line += val + \"\\n\");\n            }\n            catch (Exception ex)\n            {\n                projectOpenResult = null;\n                errorMsg = ex.Message;\n            }\n        }, $\"Opening {Path.GetFileName(filename)}...\");\n\n        if (projectOpenResult == null)\n        {\n            ProjectView.OnProjectOpenFail(errorMsg);\n            return false;\n        }\n\n        var warnings = projectOpenResult.OpenResult.Warnings;\n        if (warnings.Count > 0)\n            ProjectView.OnProjectOpenWarnings(warnings);\n\n        OnProjectOpenSuccess(filename, projectOpenResult.Root.Project);\n        return true;\n    }\n\n    private IProjectFileManager CreateProjectFileManager()\n    {\n        var projectFileManager = projectFileManagerCreate();\n        projectFileManager.RomPromptFn = AskToSelectNewRomFilename;\n        return projectFileManager;\n    }\n\n    private void OnProjectOpenSuccess(string filename, Project project)\n    {\n        ProjectView.Project = Project = project;\n        Project.PropertyChanged += Project_PropertyChanged;\n\n        ProjectChanged?.Invoke(this, new IProjectController.ProjectChangedEventArgs\n        {\n            ChangeType = IProjectController.ProjectChangedEventArgs.ProjectChangedType.Opened,\n            Filename = filename,\n            Project = project,\n        });\n    }\n\n    private void Project_PropertyChanged(object sender, PropertyChangedEventArgs e)\n    {\n        // TODO: use this to listen to interesting change events in Project/Data\n        // so we can react appropriately.\n    }\n\n    public string SaveProject(string filename)\n    {\n        try\n        {\n            var emptyFilename = string.IsNullOrEmpty(filename);\n            if (emptyFilename)\n                throw new ArgumentException(\"empty filename specified\", nameof(filename));\n\n            string err = null;\n            DoLongRunningTask(\n                () => err = CreateProjectFileManager().Save(Project, filename),\n                $\"Saving {Path.GetFileName(filename)}...\"\n            );\n\n            if (err != null)\n                return err;\n        }\n        catch (Exception ex)\n        {\n            return ex.Message;\n        }\n\n        ProjectView.OnProjectSaved();\n        return null;\n    }\n\n    public void ImportBizHawkCdl(string filename)\n    {\n        BizHawkCdlImporter.Import(filename, Project.Data.GetSnesApi() ?? throw new InvalidOperationException(\"Project has no SNES API Present\"));\n\n        ProjectChanged?.Invoke(this, new IProjectController.ProjectChangedEventArgs\n        {\n            ChangeType = IProjectController.ProjectChangedEventArgs.ProjectChangedType.Imported,\n            Filename = filename,\n            Project = Project,\n        });\n    }\n\n    public bool ImportRomAndCreateNewProject(string romFilename)\n    {\n        var importController = SetupImportController();\n        var importSettings = importController.PromptUserForImportOptions(romFilename);\n        if (importSettings == null) \n            return false;\n        \n        CloseProject();\n        ImportRomAndCreateNewProject(importSettings);\n        return true;\n    }\n\n    private void ImportRomAndCreateNewProject(ImportRomSettings importSettings)\n    {\n        var importer = projectImporterFactoryCreate.Invoke(importSettings);\n        var project = importer.Read();\n        if (project != null)\n        {\n            OnProjectOpenSuccess(project.ProjectFileName, project);   \n        }\n    }\n\n    private IImportRomDialogController SetupImportController()\n    {\n        // let the user select settings on the GUI\n        var importController = controllerFactory.GetImportRomDialogController();\n        importController.View.Controller = importController;\n        return importController;\n    }\n\n    public void ImportLabelsCsv(ILabelEditorView labelEditor, bool replaceAll)\n    {\n        var importFilename = labelEditor.PromptForCsvFilename();\n        if (string.IsNullOrEmpty(importFilename))\n            return;\n\n        var errLine = 0;\n        try\n        {\n            Project.Data.Labels.ImportLabelsFromCsv(importFilename, replaceAll, smartMerge: true, out errLine);\n            labelEditor.RepopulateFromData();\n        }\n        catch (Exception ex)\n        {\n            labelEditor.ShowLineItemError(ex.Message, errLine);\n        }\n    }\n    \n    private string AskToSelectNewRomFilename(string error) => \n        ProjectView.AskToSelectNewRomFilename(\"Error\", $\"{error}\\n\\nLink a new ROM now?\");\n\n    public void WriteAssemblyOutput()\n    {\n        WriteAssemblyOutput(Project.LogWriterSettings, true);\n    }\n\n    private void WriteAssemblyOutput(LogWriterSettings settings, bool showProgressBarUpdates = false)\n    {\n        var lc = new LogCreator\n        {\n            Settings = settings,\n            Data = new LogCreatorByteSource(Project.Data),\n        };\n\n        LogCreatorOutput.OutputResult result = null;\n        DoLongRunningTask(() => result = lc.CreateLog(), \"Exporting assembly source code...\");\n\n        ProjectView.OnExportFinished(result);\n    }\n\n    public void UpdateExportSettings(LogWriterSettings selectedSettings)\n    {\n        if (Project == null)\n            return;\n            \n        var projectHadUnsavedChanges = Project.Session?.UnsavedChanges ?? false;\n        var exportSettingsChanged = !Project.LogWriterSettings.Equals(selectedSettings);\n\n        Project.LogWriterSettings = selectedSettings;\n\n        if (Project.Session != null && exportSettingsChanged && !projectHadUnsavedChanges)\n            Project.Session.UnsavedChanges = true;\n    }\n\n    public void MarkChanged()\n    {\n        // eventually set this via INotifyPropertyChanged or similar.\n        if (Project.Session != null) Project.Session.UnsavedChanges = true;\n    }\n\n    public void SelectOffset(int offset, [CanBeNull] ISnesNavigation.HistoryArgs historyArgs = null) =>\n        ProjectView.SelectOffset(offset, historyArgs);\n\n    public void NormalizeWramLabels()\n    {\n        if (!commonGui.PromptToConfirmAction(\n                \"This converts all WRAM labels (where possible and non-overlapping) to the $7E/$7F range. Proceed?\"))\n            return;\n        \n        Project.Data.GetSnesApi()?.NormalizeWramLabels();\n    }\n    \n    public int FixMisalignedFlags()\n    {\n        var countModified = Project.Data.GetSnesApi()?.FixMisalignedFlags() ?? 0;\n        if (countModified > 0)\n            MarkChanged();\n        \n        return countModified;\n    }\n    \n    public bool RescanForInOut()\n    {\n        var snesData = Project.Data.GetSnesApi();\n        if (snesData == null)\n            return false;\n        \n        snesData.RescanInOutPoints();\n        MarkChanged();\n        return true;\n    }\n\n    public long ImportBsnesUsageMap(string fileName)\n    {\n        var snesData = Project?.Data.GetSnesApi();\n        if (snesData == null)\n            return 0;\n\n        var linesModified = 0;\n        DoLongRunningTask(() =>\n        {\n            // 1. run the BSNES import usage map\n            var importer = new BsnesUsageMapImporter(\n                usageMap: File.ReadAllBytes(fileName), \n                snesData: snesData,\n                onlyMarkIfUnreached: Project.ProjectSettings.BsnesUsageMapImportOnlyChangedUnmarked\n            );\n            linesModified = importer.Run();\n            \n            // 2. to clean it up a little, run our \"fixup\" stuff.\n            FixMisalignedFlags();\n            RescanForInOut();\n\n        }, \"Import usage map + fixup flags + rescan IN/Out\");\n        \n        if (linesModified > 0)\n            MarkChanged();\n\n        return linesModified;\n    }\n\n    public long ImportBsnesTraceLogs(string[] fileNames)\n    {\n        var importer = new BsnesTraceLogImporter(Project.Data.GetSnesApi());\n\n        // TODO: differentiate between binary-formatted and text-formatted files\n        // probably look for a newline within 80 characters\n        // call importer.ImportTraceLogLineBinary()\n\n        var largeFilesReader = controllerFactory.GetLargeFileReaderProgressController();\n\n        // caution: trace logs can be gigantic, even a few seconds can be > 1GB\n        // inside here, performance becomes critical.\n        largeFilesReader.Filenames = new List<string>(fileNames);\n        largeFilesReader.LineReadCallback = line => importer.ImportTraceLogLine(line);\n        largeFilesReader.Run();\n\n        if (importer.CurrentStats.NumRomBytesModified > 0)\n            MarkChanged();\n\n        return importer.CurrentStats.NumRomBytesModified;\n    }\n\n    public long ImportBsnesTraceLogsBinary(IEnumerable<string> filenames, BsnesTraceLogCaptureController.TraceLogCaptureSettings workItemCaptureSettings)\n    {\n        var importer = new BsnesTraceLogImporter(Project.Data.GetSnesApi());\n\n        foreach (var file in filenames)\n        {\n            using Stream source = File.OpenRead(file);\n            const int bytesPerPacket = 22;\n            var buffer = new byte[bytesPerPacket];\n            int bytesRead;\n            while ((bytesRead = source.Read(buffer, 0, bytesPerPacket)) > 0)\n            {\n                Debug.Assert(bytesRead == 22);\n                importer.ImportTraceLogLineBinary(buffer, true, workItemCaptureSettings);\n            }\n        }\n        \n        importer.CopyTempGeneratedCommentsIntoMainSnesData();\n\n        return importer.CurrentStats.NumRomBytesModified;\n    }\n        \n    public void CloseProject()\n    {\n        if (Project == null)\n            return;\n\n        ProjectChanged?.Invoke(this, new IProjectController.ProjectChangedEventArgs\n        {\n            ChangeType = IProjectController.ProjectChangedEventArgs.ProjectChangedType.Closing\n        });\n\n        Project = null;\n    }\n\n    /// <summary>\n    /// Confirm with user that the project export settings are valid, then start exporting.\n    /// </summary>\n    /// <returns>True if we exported assembly, false if we didn't / aborted.</returns>\n    public bool ConfirmSettingsThenExportAssembly()\n    {\n        var newlyEditedSettings = ShowSettingsEditorUntilValid();\n        return WriteAssemblyOutputIfSettingsValid(newlyEditedSettings);\n    }\n\n    /// <summary>\n    /// Export assembly using current project settings (fails if settings not currently valid) \n    /// </summary>\n    /// <returns>True if we exported assembly, false if we didn't / aborted.</returns>\n    public bool ExportAssemblyWithCurrentSettings() => \n        WriteAssemblyOutputIfSettingsValid() || ConfirmSettingsThenExportAssembly();\n\n    [CanBeNull]\n    public LogWriterSettings ShowSettingsEditorUntilValid()\n    {\n        LogWriterSettings newlyEditedSettings = null;\n\n        do\n        {\n            var shouldAskUserToContinue = newlyEditedSettings != null; \n            if (shouldAskUserToContinue && !PromptUserTryAgainOrAbortExport())\n                return null;\n\n            newlyEditedSettings = ShowExportSettingsEditor();\n            if (newlyEditedSettings == null)\n                return null;\n                \n        } while (!newlyEditedSettings.IsValid(fs));\n\n        return newlyEditedSettings;\n    }\n\n    private bool PromptUserTryAgainOrAbortExport() => \n        commonGui.PromptToConfirmAction(\"Can't export assembly because export settings are invalid. Edit now?\");\n\n    public bool WriteAssemblyOutputIfSettingsValid() => \n        WriteAssemblyOutputIfSettingsValid(Project?.LogWriterSettings);\n\n    public bool WriteAssemblyOutputIfSettingsValid(LogWriterSettings settingsToUseAndSave)\n    {\n        if (settingsToUseAndSave == null || !settingsToUseAndSave.IsValid(fs))\n            return false;\n        \n        // must have saved the project first\n        if (Project.Session?.ProjectDirectory.Length == 0)\n            return false;\n        \n        // save asm exporter settings \n        UpdateExportSettings(settingsToUseAndSave);\n        \n        // OPTIONAL: save the project file, just in case anything goes wrong during export\n        if (Project?.ProjectFileName != \"\")\n            SaveProject(Project?.ProjectFileName);\n        \n        // do the real output\n        WriteAssemblyOutput();\n        \n        return true;\n    }\n\n    [CanBeNull]\n    private LogWriterSettings ShowExportSettingsEditor()\n    {\n        var exportSettingsController = CreateExportSettingsEditorController();\n        return !(exportSettingsController?.PromptSetupAndValidateExportSettings() ?? false) \n            ? null \n            : exportSettingsController.Settings;\n    }\n    \n    [CanBeNull]\n    private ILogCreatorSettingsEditorController CreateExportSettingsEditorController()\n    {\n        if (Project == null)\n            return null;\n        \n        var exportSettingsController = controllerFactory.GetAssemblyExporterSettingsController();\n        exportSettingsController.KeepPathsRelativeToThisPath = Project.Session?.ProjectDirectory ?? \"\";\n        exportSettingsController.Settings = Project.LogWriterSettings with { }; // operate on a new copy of the settings\n        return exportSettingsController;\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/ProjectOpenerGuiController.cs",
    "content": "﻿namespace Diz.Controllers.controllers;\n\n// this is for Diz3, re-enable if still useful later.\n\n/*public class ProjectOpenerGuiController\n{\n    private readonly IProgressView progressView;\n\n    public ProjectOpenerGuiController(IProgressView progressView)\n    {\n        this.progressView = progressView;\n    }\n\n    public IProjectOpenerHandler Handler { get; init; }\n\n    public Project OpenProject(string filename)\n    {\n        Project project = null;\n        var errorMsg = \"\";\n        var warningMsg = \"\";\n\n        DoLongRunningTask(() =>\n        {\n            try\n            {\n                var (project1, warning) = new ProjectFileManager()\n                {\n                    RomPromptFn = Handler.AskToSelectNewRomFilename\n                }.Open(filename);\n\n                project = project1;\n                warningMsg = warning;\n            }\n            catch (AggregateException ex)\n            {\n                project = null;\n                errorMsg = ex.InnerExceptions.Select(e => e.Message).Aggregate((line, val) => line += val + \"\\n\");\n            }\n            catch (Exception ex)\n            {\n                project = null;\n                errorMsg = ex.Message;\n            }\n        }, $\"Opening {Path.GetFileName(filename)}...\");\n            \n        if (project == null)\n        {\n            Handler.OnProjectOpenFail(errorMsg);\n            return null;\n        }\n\n        if (warningMsg != \"\")\n            Handler.OnProjectOpenWarning(warningMsg);\n            \n        Handler.OnProjectOpenSuccess(filename, project);\n        return project;\n    }\n\n    private void DoLongRunningTask(Action task, string description)\n    {\n        if (Handler.TaskHandler != null)\n            Handler.TaskHandler(task, description, progressView);\n        else\n            task();\n    }\n}*/"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/ProjectsController.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing Diz.Controllers.interfaces;\nusing Diz.Core;\nusing Diz.Core.model;\n\nnamespace Diz.Controllers.controllers\n{\n    /// <summary>\n    /// Loads and caches Project objects, and stores references to the last loaded project.\n    /// </summary>\n    public class ProjectsManager : IProjectsManager\n    {\n        private readonly IProjectLoader projectLoader;\n\n        public Dictionary<string, Project> LoadedProjects { get; } = new();\n        \n        // this needs some rework, it's used all over the place in odd ways.\n        public static string LastOpenedProjectFilename { get; set; }\n\n        public ProjectsManager(IProjectLoader projectLoader)\n        {\n            this.projectLoader = projectLoader;\n        }\n\n        /// <summary>\n        /// Get a loaded project associated with the filename\n        /// If it's already loaded, we'll return a copy of the loaded project. This may\n        /// be shared with already opened views.  If it's not loaded, load from disk.\n        /// </summary>\n        /// <param name=\"filename\"></param>\n        /// <returns></returns>\n        public Project GetProject(string filename)\n        {\n            if (string.IsNullOrEmpty(filename))\n                return null;\n            \n            if (LoadedProjects.ContainsKey(filename))\n                return LoadedProjects[filename];\n            \n            var project = projectLoader.LoadProject(filename);\n            if (project == null)\n                return null;\n            \n            LoadedProjects.Add(filename, project);\n            return project;\n        }\n\n        /// <summary>\n        /// Get a sample project, loaded from hardcoded data inside the app itself. Suitable for testing/demo purposes\n        /// and doesn't need to read anything from the disk.\n        /// </summary>\n        /// <returns>Demo project with demo ROM loaded</returns>\n        public Project GetSampleProject() => \n            GetProject(ProjectLoaderWithSampleDataDecorator.MagicSampleProjectName);\n\n        public Project GetLastOpenedProject()\n        {\n            // this is kinda messy/dumb, rethink.\n            Debug.Assert(LoadedProjects.Values.Count == 1);\n            return LoadedProjects.Values.Select(project => project).FirstOrDefault();\n        }\n        \n        public void OpenProjectFile(string filename)\n        {\n            if (filename == null)\n                return;\n            \n            AfterProjectOpenAttempt(filename, GetProject(filename));\n        }\n\n        public void OpenLastLoadedProject() => \n            AfterProjectOpenAttempt(\"\", GetLastOpenedProject());\n\n        private void AfterProjectOpenAttempt(string filenameAttempted, Project loadedProject)\n        {\n            if (loadedProject == null)\n            {\n                var filenameInfo = !string.IsNullOrEmpty(filenameAttempted)\n                    ? $\" (filename={filenameAttempted})\"\n                    : \"\";\n                \n                throw new InvalidDataException($\"Failed to open project{filenameInfo}\");\n            }\n            \n            OnProjectOpened?.Invoke(this, loadedProject);\n        }\n        \n        public event EventHandler<Project> OnProjectOpened;\n    }\n\n    /// <summary>\n    /// Interface to Load Project objects\n    /// </summary>\n    public interface IProjectLoader\n    {\n        public Project LoadProject(string filename);\n    }\n\n    /// <summary>\n    /// A loader that reads from disk\n    /// </summary>\n    public class ProjectFileLoader : IProjectLoader\n    {\n        public IProjectOpenerHandler ProjectOpenerHandler { get; init; }\n\n        public Project LoadProject(string filename)\n        {\n            return ProjectOpenerHandler?.OpenProject(filename, showPopupAlertOnLoaded: false);   \n        }\n    }\n    \n    /// <summary>\n    /// A decorator for IProjectLoader that returns a project created from our internal sample data\n    /// (instead of data from disk). Pass in the special string constant here to use.\n    /// TODO: integrate this with the other ISnesSampleProjectFactory interface, they duplicate the same functionality.\n    /// </summary>\n    public class ProjectLoaderWithSampleDataDecorator : IProjectLoader\n    {\n        // pass in this magic string to load the \"sample data\" project.\n        // great for testing and for showing sample stuff in the app.\n        // \n        // ReSharper disable once MemberCanBeProtected.Global\n        public const string MagicSampleProjectName = \"sampleproject111111112\";\n        \n        private readonly IProjectLoader previous;\n        private readonly ISampleDataFactory sampleDataFactory;\n\n        public ProjectLoaderWithSampleDataDecorator(IProjectLoader previous, ISampleDataFactory sampleDataFactory)\n        {\n            Debug.Assert(previous != null);\n            this.previous = previous;\n            this.sampleDataFactory = sampleDataFactory;\n        } \n\n        public Project LoadProject(string filename)\n        {\n            return filename != MagicSampleProjectName \n                ? previous.LoadProject(filename)\n                : CreateNewSampleProject();\n        }\n\n        private Project CreateNewSampleProject()\n        {\n            var project = new Project \n            {\n                Data = sampleDataFactory.Create(),\n            };\n\n            project.Session = new ProjectSession(project, MagicSampleProjectName);\n            \n            return project;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/controllers/StartFormController.cs",
    "content": "﻿// using System;\n// using System.Diagnostics;\n// using Diz.Controllers.interfaces;\n//\n// namespace Diz.Controllers.controllers\n// {\n//     public class StartFormController : IStartFormController\n//     {\n//         public IStartFormViewer View { get; }\n//\n//\n//         public StartFormController(IStartFormViewer view)\n//         {\n//             Debug.Assert(view != null);\n//             View = view;\n//             View.Closed += ViewOnClosed;\n//         }\n//\n//         public void Show() => View.Show();\n//         \n//         [Obsolete]\n//         public event EventHandler Closed;\n//         \n//         private void ViewOnClosed(object sender, EventArgs e) => Closed?.Invoke(this, e);\n//     }\n// }\n"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/interfaces/IControllers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing Diz.Controllers.controllers;\nusing Diz.Core;\nusing Diz.Core.commands;\nusing Diz.Core.export;\nusing Diz.Core.model;\nusing Diz.Core.util;\nusing Diz.Cpu._65816;\nusing JetBrains.Annotations;\n\n// NOTE: lots of these interfaces were created temporarily for major refactoring.\n// when that process is finished, we should probably take a pass here to simplify anything\n// that ended up being unnecessary or over-complicated\n\nnamespace Diz.Controllers.interfaces;\n\n// public interface IStartFormController : IFormViewer\n// {\n//     public IStartFormViewer View { get; }\n// }\n//     \n\npublic interface IProjectController : \n    ITraceLogImporters,\n    IFixInstructionUtils,\n    IDataUtilities\n{\n    // diz3.0 is going to need some major surgery from this one.\n\n    public Project Project { get; }\n        \n    public class ProjectChangedEventArgs\n    {\n        public enum ProjectChangedType\n        {\n            Invalid,\n            Saved,\n            Opened,\n            Imported,\n            Closing\n        }\n\n        public ProjectChangedType ChangeType;\n        public Project Project;\n        public string Filename;\n    }\n                \n    delegate void ProjectChangedEvent(object sender, ProjectChangedEventArgs e);\n    event ProjectChangedEvent ProjectChanged;\n\n    IProjectView ProjectView { get; set; }\n\n    bool OpenProject(string filename);  // older signature\n    string SaveProject(string filename); // older signature. new should return void\n\n    bool ImportRomAndCreateNewProject(string romFilename);\n    void ImportLabelsCsv(ILabelEditorView labelEditor, bool replaceAll);\n    void SelectOffset(int offset, [CanBeNull] ISnesNavigation.HistoryArgs historyArgs = null);\n\n    bool ConfirmSettingsThenExportAssembly();\n    bool ExportAssemblyWithCurrentSettings();\n    void MarkChanged(); // rename to MarkUnsaved or similar in Diz3.0\n}\n    \npublic interface IProjectOpenerHandler : ILongRunningTaskHandler\n{\n    public void OnProjectOpenSuccess(string filename, Project project);\n    public void OnProjectOpenWarnings(IReadOnlyList<string> warnings);\n    public void OnProjectOpenFail(string fatalError);\n    public string AskToSelectNewRomFilename(string error);\n        \n    Project OpenProject(string filename, bool showPopupAlertOnLoaded);\n}\n\npublic interface IExportDisassembly\n{\n    void UpdateExportSettings(LogWriterSettings selectedSettings);\n    void WriteAssemblyOutput();\n}\n\npublic interface IFixInstructionUtils\n{\n    // probably combine this with something else.\n    // not sure that this should really be an interface but...\n    bool RescanForInOut();\n}\n\npublic interface ITraceLogImporters\n{\n    void ImportBizHawkCdl(string filename);\n    long ImportBsnesUsageMap(string fileName);\n    long ImportBsnesTraceLogs(string[] fileNames);\n}\n\npublic interface IProjectNavigation\n{\n    public int SelectedSnesOffset { get; set; }\n\n    void GoTo(int offset);\n    void GoToUnreached(bool end, bool direction);\n    void GoToIntermediateAddress(int offset);\n    // void OnUserChangedSelection(ByteEntry newSelection);\n}\n\npublic interface ILabelImporter\n{\n    void ImportLabelsCsv(ILabelEditorView labelEditor, bool replaceAll);\n}\n\n#if DIZ_3_BRANCH\n    public interface IMainFormController : \n        \n        IFormController,\n        \n        // TODO: shouldn't have the word 'Grid' in here for Main Form controller. refactor\n        // either naming or functionality.\n        // IBytesGridViewerDataController<RomByteDataGridRow>,\n        \n        IProjectController,\n        I65816CpuOperations, \n        IExportDisassembly, \n        IProjectOpenerHandler, \n        ITraceLogImporters, \n        IProjectNavigation,\n        ILabelImporter\n    {\n        public FlagType CurrentMarkFlag { get; set; }\n        public bool MoveWithStep { get; set; }\n        \n        void SetProject(string filename, Project project);\n    }\n#endif\n\npublic interface IMarkManyController<out TDataSource>\n{\n    IDataRange DataRange { get; }\n    TDataSource Data { get; }\n    MarkCommand GetMarkCommand();\n}\n\npublic interface ILogCreatorSettingsEditorController : INotifyPropertyChangedExt\n{\n    ILogCreatorSettingsEditorView View { get; set; }\n    \n    LogWriterSettings Settings { get; set; }\n\n    public string KeepPathsRelativeToThisPath { get; set; }\n\n    bool PromptSetupAndValidateExportSettings();\n\n    bool EnsureSelectRealOutputDirectory(bool forcePrompt = false);\n    string GetSampleOutput();\n}\n    \n    \npublic interface IDizAppSettings : INotifyPropertyChanged\n{\n    string LastProjectFilename { get; set; }\n    bool OpenLastFileAutomatically { get; set; }\n}\n\npublic interface IDizDocument : INotifyPropertyChanged\n{\n    Project Project { get; set; }\n    string LastProjectFilename { get; set; }\n    public BindingList<NavigationEntry> NavigationHistory { get; set; }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/interfaces/IViewers.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Diz.Controllers.controllers;\nusing Diz.Core.commands;\nusing Diz.Core.Interfaces;\nusing JetBrains.Annotations;\n\nnamespace Diz.Controllers.interfaces;\n\npublic interface IFormViewer\n{\n    public event EventHandler OnFormClosed;\n    // void Close();\n    void Show();\n    void BringFormToTop();\n}\n\npublic interface IModalDialog\n{\n    /// <summary>\n    /// Show the dialog to the user and wait for them to complete\n    /// the steps on the view\n    /// </summary>\n    /// <returns>True if steps were completed and we have a valid result</returns>\n    bool PromptDialog();\n}\n    \n\npublic interface IProgressView : IFormViewer, IModalDialog, IProgress<int> {\n    public bool IsMarquee { get; set; }\n    public string TextOverride { get; set; }\n    bool IsVisible();\n        \n    /// <summary>\n    /// Signal that a job (potentially running in another task/thread) has completed.\n    /// CAUTION: Implementers should use thread-safety measures, this may be called\n    /// from a different thread than any other calls \n    /// </summary>\n    void SignalJobIsDone();\n}\n\n// diz2 version (use it)\npublic interface IMarkManyView<TDataSource> : IModalDialog \n    where TDataSource : IRomSize\n{\n    MarkCommand.MarkManyProperty Property { get; set; }\n    object GetPropertyValue();\n    [CanBeNull] IMarkManyController<TDataSource> Controller { get; set; }\n\n    void AttemptSetSettings(Dictionary<MarkCommand.MarkManyProperty, object> settings);\n    Dictionary<MarkCommand.MarkManyProperty, object> SaveCurrentSettings();\n}\n\n\n#if DIZ_3_BRANCH\n    public interface IBytesGridViewer<TByteItem> : IRowBaseViewer<TByteItem>, IViewer\n    {\n        public List<TByteItem> DataSource { get; set; }\n        int TargetNumberOfRowsToShow { get; }\n\n        void SelectRow(int row);\n        \n\n        void BeginEditingSelectionComment();\n        void BeginEditingSelectionLabel();\n        \n        public class SelectedOffsetChangedEventArgs : EventArgs\n        {\n            public TByteItem Row { get; init; }\n            public int RowIndex { get; init; }\n        }\n\n        public delegate void SelectedOffsetChange(object sender, SelectedOffsetChangedEventArgs e);\n\n        public event SelectedOffsetChange SelectedOffsetChanged;\n    }\n#endif\n    \npublic interface ILabelEditorView : IFormViewer\n{\n    // a lot of these fields/methods shouldn't be done this way\n        \n    string PromptForCsvFilename(); // get rid of\n    void ShowLineItemError(string exMessage, int errLine);  // get rid of\n    \n    void SetProjectController([CanBeNull] IProjectController projectController);\n    void RepopulateFromData(); // keep\n    void RebindProject(); // keep\n\n    void FocusOrCreateLabelAtSelectedRomOffsetIa();\n    void FocusOrCreateLabelAtRomOffsetIa(int selectedOffset);\n    void FocusOrCreateLabelAtSnesAddress(int snesAddress);\n}\n\npublic interface IRegionListView : IFormViewer\n{\n    void SetProjectController([CanBeNull] IProjectController projectController);\n    void RebindProject();\n}\n    \npublic interface IImportRomDialogView\n{\n    IImportRomDialogController Controller { get; set; }\n    public List<string> EnabledVectorTableEntries { get; }\n        \n    bool ShowAndWaitForUserToConfirmSettings();\n    void RefreshUi();\n}\n\npublic interface ICommonGui\n{\n    bool PromptToConfirmAction(string msg);\n        \n    void ShowError(string msg);\n    void ShowWarning(string msg);\n    void ShowMessage(string msg);\n}\n    \npublic interface ILogCreatorSettingsEditorView : IFormViewer\n{\n    ILogCreatorSettingsEditorController Controller { get; set; }\n    \n    [CanBeNull] string PromptForLogPathFromFileOrFolderDialog(bool askForFile);\n    bool PromptCreatePath(string buildFullOutputPath, string extraMsg);\n        \n    /// <summary>\n    /// Main method, return true if we showed the dialog and edited successfully.\n    /// </summary>\n    /// <returns></returns>\n    bool PromptEditAndConfirmSettings();\n}\n    \n#if DIZ_3_BRANCH\n    public interface IDataGridEditorForm : IFormViewer, IProjectView\n    {\n        IMainFormController MainFormController { get; set; }\n    }\n#endif"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/interfaces/Interfaces.cs",
    "content": "﻿using System;\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.util;\nusing Diz.Core.model;\n// using Diz.Core.model.byteSources;\nusing Diz.Core.model.snes;\n\nnamespace Diz.Controllers.interfaces;\n\npublic interface IDizApp\n{\n    void Run(string initialProjectFileToOpen = \"\");\n}\n    \npublic interface IGridRow<out TItem>\n{\n    Data Data { get; init; }\n    TItem Item { get; }\n}\n \n#if DIZ_3_BRANCH\n    public interface IDataGridRow : IGridRow<ByteEntry>, INotifyPropertyChanged\n    {\n        \n    }\n\n    public interface IDataSubsetRomByteDataGridLoader<TRow, TItem> : IDataSubsetLoader<TRow, TItem>\n    {\n        // probably this needs to be refactored away, this exists for dependency injection resolution only\n        // across the Controller and Gui layer\n        \n        public IBytesGridViewer<TItem> View { get; set; }\n        public Data Data { get; set; }\n    }\n#endif\n    \npublic interface IProjectsManager : IProjects, IProjectLoadListener, ISampleProjectLoader, ILastProjectLoaded { }\n\npublic interface IProjectLoadListener\n{\n    public event EventHandler<Project> OnProjectOpened;\n    void OpenProjectFile(string filename);\n}\n\npublic interface IProjects\n{\n    Project GetProject(string filename);\n}\n\npublic interface ISampleProjectLoader\n{\n    Project GetSampleProject();\n}\n    \npublic interface ILastProjectLoaded\n{\n    Project GetLastOpenedProject();\n    void OpenLastLoadedProject();\n}\n    \n// note: this is an autofactory, so the names of the methods map to registrations (strings)\npublic interface IViewFactory\n{\n    IImportRomDialogView GetImportRomView();\n    IProgressView GetProgressBarView();\n    ILogCreatorSettingsEditorView GetExportDisassemblyView();\n    ILabelEditorView GetLabelEditorView();\n    IMainGridWindowView GetMainGridWindowView();\n    IFormViewer GetAboutView();\n    IRegionListView GetRegionEditorView();\n}\n    \npublic interface IControllerFactory\n{\n    ILogCreatorSettingsEditorController GetAssemblyExporterSettingsController();\n    IImportRomDialogController GetImportRomDialogController();\n    ILargeFilesReaderController GetLargeFileReaderProgressController();\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/services/Registration.cs",
    "content": "﻿// using Diz.Controllers.controllers;\n\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Controllers.util;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.Controllers.services;\n\n[UsedImplicitly]\npublic class DizControllersCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.Register<IProjectController, ProjectController>(\"ProjectController\");\n        serviceRegistry.Register<ILogCreatorSettingsEditorController, LogCreatorSettingsEditorController>(\"AssemblyExporterSettingsController\");\n        serviceRegistry.Register<IImportRomDialogController, ImportRomDialogController>(\"ImportRomDialogController\");\n        serviceRegistry.Register<ILargeFilesReaderController, LargeFilesReader>(\"LargeFileReaderProgressController\");\n        \n        serviceRegistry.EnableAutoFactories();\n        serviceRegistry.RegisterAutoFactory<IControllerFactory>();\n\n        serviceRegistry.Register<IDizDocument, DizDocument>();\n\n        // sorry this is all a huge WIP mess, cleanup incoming soon.\n\n        // serviceRegistry.Register<int, int, IReadOnlySnesRom, IMarkManyController>(\n        //     (factory, offset, whichIndex, data) =>\n        //     {\n        //          // TODO: update this with updated controller from Diz 2.0 branch.\n        //          // I think that means kill 'whichIndex', use the new format that doesn't rely on it.\n        //         var view = factory.GetInstance<IMarkManyView>();\n        //         var markManyController = new MarkManyController(offset, whichIndex, data, view);\n        //         markManyController.MarkManyView.Controller = markManyController;\n        //         return markManyController;\n        //     });\n\n        // TODO: might be able to make some of these register using\n        // \"open generics\" to be more flexible.\n\n#if DIZ_3_BRANCH\n            serviceRegistry.Register(\n                typeof(IDataController), \n                typeof(RomByteDataBindingController<IGridRow<ByteEntry>>)\n            );\n\n            serviceRegistry.Register<IDataController, RomByteDataBindingGridController>();\n\n            serviceRegistry.Register(\n                typeof(IBytesGridDataController<,>),\n                typeof(RomByteDataBindingController<>)\n            );\n            \n            serviceRegistry.Register(\n                typeof(IBytesGridDataController<IDataGridRow,ByteEntry>),\n                typeof(RomByteDataBindingGridController)\n            );\n            \n            serviceRegistry.Register<IStartFormController, StartFormController>();\n            \n            serviceRegistry.Register<IMainFormController, MainFormController>();\n            \n            serviceRegistry.Register<IProjectLoader, ProjectFileLoader>();\n            serviceRegistry.Decorate(\n                typeof(IProjectLoader), \n                typeof(ProjectLoaderWithSampleDataDecorator));\n\n            serviceRegistry.Register<IProjectsManager, ProjectsManager>();\n            \n            serviceRegistry.RegisterSingleton<ISampleProjectLoader, ProjectsManager>(\"SampleProjectLoader\");\n#endif\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/util/DizDocument.cs",
    "content": "﻿#nullable enable\nusing System.ComponentModel;\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Controllers.util;\n\n/// <summary>\n/// This class is for GUI internal use only, it should never be serialized.\n/// It represents the current \"open document\" (usually a thin wrapper around a Project)\n/// Anything that should persist should go inside Project instead.\n///\n/// This can store some per-user settings that get saved locally.\n/// Don't save anything important here though.\n/// </summary>\n[UsedImplicitly]\npublic class DizDocument : IDizDocument\n{\n    private readonly IDizAppSettings appSettings;\n    \n    public Project? Project\n    {\n        get => project;\n        set => this.SetField(PropertyChanged, ref project, value, compareRefOnly: true);\n    }\n    private Project? project;\n\n    public string LastProjectFilename\n    {\n        get => appSettings.LastProjectFilename;\n        set\n        {\n            var projectName = appSettings.LastProjectFilename;\n            this.SetField(PropertyChanged, ref projectName, value);\n            appSettings.LastProjectFilename = projectName;\n        }\n    }\n\n    public BindingList<NavigationEntry> NavigationHistory\n    {\n        get => navigationHistory;\n        set => this.SetField(PropertyChanged, ref navigationHistory, value);\n    }\n\n    private BindingList<NavigationEntry> navigationHistory = new()\n    {\n        RaiseListChangedEvents = true,\n        AllowNew = false,\n        AllowRemove = false,\n        AllowEdit = false,\n    };\n\n    public DizDocument(IDizAppSettings appSettings)\n    {\n        this.appSettings = appSettings;\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/util/LargeFilesReader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.util;\n\nnamespace Diz.Controllers.util\n{\n    public class LargeFilesReader : ProgressBarWorker, ILargeFilesReaderController\n    {\n        public IReadOnlyCollection<string> Filenames { get; set; }\n        public Action<string> LineReadCallback { get; set; }\n\n        protected long SumFileLengthsInBytes { get; set; }\n        protected long BytesReadFromPreviousFiles { get; set; }\n\n        protected override void Thread_DoWork()\n        {\n            SumFileLengthsInBytes = 0L;\n            foreach (var filename in Filenames)\n            {\n                SumFileLengthsInBytes += Util.GetFileSizeInBytes(filename);\n            }\n\n            BytesReadFromPreviousFiles = 0L;\n            foreach (var filename in Filenames)\n            {\n                using var fs = File.Open(filename, FileMode.Open, FileAccess.Read);\n                using var bs = new BufferedStream(fs);\n                using var sr = new StreamReader(bs);\n                string line;\n                while ((line = sr.ReadLine()) != null)\n                {\n                    LineReadCallback(line);\n                    UpdateProgress(fs.Position);\n                }\n\n                BytesReadFromPreviousFiles += fs.Length;\n            }\n        }\n\n        private int previousProgress;\n\n        protected void UpdateProgress(long currentPositionInBytes)\n        {\n            var percent = (BytesReadFromPreviousFiles + currentPositionInBytes) / (float)SumFileLengthsInBytes;\n            var progressValue = (int)(percent * 100);\n\n            if (progressValue <= previousProgress)\n                return;\n\n            // don't do this too often, kinda slow due to thread synchronization.\n            base.UpdateProgress(progressValue);\n\n            previousProgress = progressValue;\n        }\n\n        public LargeFilesReader(IProgressView view) : base(view) { }\n    }\n\n    public interface ILargeFilesReaderController\n    {\n        IReadOnlyCollection<string> Filenames { get; set; }\n        Action<string> LineReadCallback { get; set; }\n        void Run();\n    }\n}\n"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers/src/util/RomByteRowBase.cs",
    "content": "﻿// using Diz.Core.model.byteSources;\n\n#if DIZ_3_BRANCH\nnamespace Diz.Controllers.util\n{\n    public class RomByteRowBase : INotifyPropertyChangedExt\n    {\n        [DisplayName(\"Label\")]\n        [Editable(true)]\n        // [CellStyleFormatter(GetBackColorInOut)]\n        public string Label\n        {\n            get => Data.Labels.GetLabelName(Data.ConvertPCtoSnes(ByteEntry.ParentIndex));\n\n            // todo (validate for valid label characters)\n            // (note: validation implemented in Furious's branch, integrate here)\n            set\n            {\n                Data.Labels.AddLabel(\n                    Data.ConvertPCtoSnes(ByteEntry.ParentIndex),\n                    new Label {Name = value},\n                    true);\n                OnPropertyChanged();\n            }\n        }\n\n        [DisplayName(\"PC\")]\n        [ReadOnly(true)]\n        public string Offset => Util.ToHexString6(Data.ConvertPCtoSnes(ByteEntry.ParentIndex));\n\n        // show the byte two different ways: ascii and numeric\n        [DisplayName(\"@\")]\n        [ReadOnly(true)]\n        public char AsciiCharRep => \n            ByteEntry?.Byte == null ? ' ' : ByteEntry.Byte.ToString()[0];\n\n        [DisplayName(\"#\")]\n        [ReadOnly(true)]\n        public string NumericRep =>\n            Util.NumberToBaseString(ByteEntry.ParentIndex, NumberBase);\n\n        [DisplayName(\"<*>\")]\n        [ReadOnly(true)]\n        public string Point =>\n            RomUtil.PointToString(ByteEntry.Point);\n\n        [DisplayName(\"Instruction\")]\n        [ReadOnly(true)]\n        public string Instruction\n        {\n            get\n            {\n                // NOTE: this does not handle instructions whose opcodes cross banks correctly.\n                // if we hit this situation, just return empty for the grid, it's likely real instruction won't do this?\n                var romOffset = ByteEntry.ParentIndex;\n                var len = Data.GetInstructionLength(romOffset);\n                return romOffset + len <= Data.GetRomSize() ? Data.GetInstruction(romOffset) : \"\";\n            }\n        }\n\n        [DisplayName(\"IA\")]\n        [ReadOnly(true)]\n        // ReSharper disable once InconsistentNaming\n        public string IA\n        {\n            get\n            {\n                var ia = Data.GetIntermediateAddressOrPointer(ByteEntry.ParentIndex);\n                return ia >= 0 ? Util.ToHexString6(ia) : \"\";\n            }\n        }\n\n        [DisplayName(\"Flag\")]\n        [ReadOnly(true)]\n        public string TypeFlag =>\n            Util.GetEnumDescription(Data.GetFlag(ByteEntry.ParentIndex));\n\n        [DisplayName(\"B\")]\n        [Editable(true)]\n        public string DataBank\n        {\n            get => Util.NumberToBaseString(Data.GetDataBank(ByteEntry.ParentIndex), Util.NumberBase.Hexadecimal, 2);\n            set\n            {\n                if (!int.TryParse(value, NumberStyles.HexNumber, null, out var parsed))\n                    return;\n\n                Data.SetDataBank(ByteEntry.ParentIndex, parsed);\n                OnPropertyChanged();\n            }\n        }\n\n        [DisplayName(\"D\")]\n        [Editable(true)]\n        public string DirectPage\n        {\n            get => Util.NumberToBaseString(Data.GetDirectPage(ByteEntry.ParentIndex), Util.NumberBase.Hexadecimal, 4);\n            set\n            {\n                if (!int.TryParse(value, NumberStyles.HexNumber, null, out var parsed))\n                    return;\n\n                Data.SetDirectPage(ByteEntry.ParentIndex, parsed);\n                OnPropertyChanged();\n            }\n        }\n\n        [DisplayName(\"M\")]\n        [Editable(true)]\n        public string MFlag\n        {\n            get => RomUtil.BoolToSize(Data.GetMFlag(ByteEntry.ParentIndex));\n            set\n            {\n                Data.SetMFlag(ByteEntry.ParentIndex, value is \"8\" or \"M\");\n                OnPropertyChanged();\n            }\n        }\n\n        [DisplayName(\"X\")]\n        [Editable(true)]\n        public string XFlag\n        {\n            get => RomUtil.BoolToSize(Data.GetXFlag(ByteEntry.ParentIndex));\n            set\n            {\n                Data.SetXFlag(ByteEntry.ParentIndex, value is \"8\" or \"X\");\n                OnPropertyChanged();\n            }\n        }\n\n        [DisplayName(\"Comment\")]\n        [Editable(true)]\n        public string Comment\n        {\n            get => Data.GetCommentText(Data.ConvertPCtoSnes(ByteEntry.ParentIndex));\n            set\n            {\n                Data.AddComment(Data.ConvertPCtoSnes(ByteEntry.ParentIndex), value, true);\n                OnPropertyChanged();\n            }\n        }\n        \n        private readonly ByteEntry byteEntry;\n\n        [Browsable(false)]\n        public ByteEntry ByteEntry\n        {\n            get => byteEntry;\n            init\n            {\n                this.SetField(PropertyChanged, ref byteEntry, value);\n                // tmp disable // if (ByteOffset != null)\n                // ByteOffset.PropertyChanged += OnRomBytePropertyChanged;\n            }\n        }\n\n        [Browsable(false)] public Data Data { get; init; }\n        [Browsable(false)] public IRowBaseViewer<ByteEntry> ParentView { get; init; }\n        [Browsable(false)] private Util.NumberBase NumberBase => \n            ParentView?.NumberBaseToShow ?? Util.NumberBase.Hexadecimal;\n\n        [Browsable(false)] public event PropertyChangedEventHandler PropertyChanged;\n\n        private void OnRomBytePropertyChanged(object sender, PropertyChangedEventArgs e)\n        {\n            void OnInstructionRelatedChanged()\n            {\n                OnPropertyChanged(nameof(Instruction));\n                OnPropertyChanged(nameof(IA));\n            }\n\n            // NOTE: if any properties under ByteOffset change, make sure the names update here\n            switch (e.PropertyName)\n            {\n                case nameof(ByteEntry.Byte):\n                    OnPropertyChanged(nameof(AsciiCharRep));\n                    OnPropertyChanged(nameof(NumericRep));\n                    OnInstructionRelatedChanged();\n                    break;\n                case nameof(ByteEntry.Arch):\n                    OnInstructionRelatedChanged();\n                    break;\n                case nameof(ByteEntry.DataBank):\n                case nameof(ByteEntry.DirectPage):\n                case nameof(ByteEntry.XFlag):\n                case nameof(ByteEntry.MFlag):\n                case nameof(ByteEntry.TypeFlag):\n                case nameof(ByteEntry.Point):\n                    OnPropertyChanged(e.PropertyName);\n                    break;\n            }\n        }\n\n        [NotifyPropertyChangedInvocator]\n        public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => \n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n        \n        #region Formatting\n        protected Color? GetBackgroundColorForMarkedAsOpcode(string colPropName)\n        {\n            // TODO: eventually, don't match strings here.\n            // instead, look for the appropriate attribute attached to romByteRow and let that \n            // attribute hook in here.\n            return colPropName switch\n            {\n                nameof(Point) => GetBackColorInOut(),\n                nameof(Instruction) => GetInstructionBackgroundColor(),\n                nameof(DataBank) => GetDataBankColor(),\n                nameof(DirectPage) => GetDirectPageColor(),\n                nameof(MFlag) => GetMFlagColor(),\n                nameof(XFlag) => GetXFlagColor(),\n                _ => null\n            };\n        }\n\n        private Color? GetBackColorInOut()\n        {\n            int r = 255, g = 255, b = 255;\n            if ((ByteEntry.Point & (InOutPoint.EndPoint | InOutPoint.OutPoint)) != 0) g -= 50;\n            if ((ByteEntry.Point & InOutPoint.InPoint) != 0) r -= 50;\n            if ((ByteEntry.Point & InOutPoint.ReadPoint) != 0) b -= 50;\n            return Color.FromArgb(r, g, b);\n        }\n\n        private Color? GetInstructionBackgroundColor()\n        {\n            var opcode = ByteEntry.Byte;\n            var isWeirdInstruction =\n                    opcode == 0x40 || opcode == 0xCB || opcode == 0xDB || opcode == 0xF8 || // RTI WAI STP SED\n                    opcode == 0xFB || opcode == 0x00 || opcode == 0x02 || opcode == 0x42 // XCE BRK COP WDM\n                ;\n            return isWeirdInstruction ? Color.Yellow : null;\n        }\n\n        private Color? GetDataBankColor()\n        {\n            switch (ByteEntry.Byte)\n            {\n                // PLB MVP MVN\n                case 0xAB:\n                case 0x44:\n                case 0x54:\n                    return Color.OrangeRed;\n                // PHB\n                case 0x8B:\n                    return Color.Yellow;\n                default:\n                    return null;\n            }\n        }\n\n        private Color? GetDirectPageColor()\n        {\n            switch (ByteEntry.Byte)\n            {\n                // PLD TCD\n                case 0x2B:\n                case 0x5B:\n                    return Color.OrangeRed;\n\n                // PHD TDC\n                case 0x0B:\n                case 0x7B:\n                    return Color.Yellow;\n\n                default:\n                    return null;\n            }\n        }\n\n        public Color? GetMFlagColor() => GetMxFlagColor(0x20);\n        public Color? GetXFlagColor() => GetMxFlagColor(0x10);\n\n        private Color? GetMxFlagColor(int nextByteMask)\n        {\n            var nextByte = Data.GetNextRomByte(ByteEntry.ParentIndex) ?? 0;\n            switch (ByteEntry.Byte)\n            {\n                // PLP\n                // SEP REP, *iff* relevant bit is set on next byte\n                case 0x28:\n                case 0xC2 or 0xE2 when (nextByte & nextByteMask) != 0:\n                    return Color.OrangeRed;\n                case 0x08: // PHP\n                    return Color.Yellow;\n                default:\n                    return null;\n            }\n        }\n        \n        #endregion\n    }\n\n    public static class RomByteRowAttributes\n    {\n        public static bool IsColumnEditable(string propertyName) => TestAttribute((EditableAttribute attr) => attr?.AllowEdit ?? false, propertyName);\n\n        public static string GetColumnDisplayName(string propertyName) => TestAttribute((DisplayNameAttribute attr) => attr?.DisplayName, propertyName);\n\n        public static bool GetColumnIsReadOnly(string propertyName) => TestAttribute((ReadOnlyAttribute attr) => attr?.IsReadOnly ?? false, propertyName);\n\n        public static bool IsPropertyBrowsable(string propertyName) => TestAttribute((BrowsableAttribute attr) => attr?.Browsable ?? true, propertyName);\n\n        private static TResult TestAttribute<TAttribute, TResult>(\n            Func<TAttribute, TResult> getValueFn, string memberName)\n            where TAttribute : Attribute\n        {\n            return Util.GetPropertyAttribute(getValueFn, typeof(RomByteRowBase), memberName);\n        }\n    }\n\n    public interface IRowBaseViewer<out TItem>\n    {\n        Util.NumberBase NumberBaseToShow { get; }\n        TItem SelectedByteOffset { get; }\n    }\n}\n#endif"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers.Test/Diz.Controllers.Test/Diz.Controllers.Test.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <Nullable>enable</Nullable>\n        <IsPackable>false</IsPackable>\n    </PropertyGroup>\n\n    <ItemGroup>\n        <PackageReference Include=\"xunit\" Version=\"2.6.6\" />\n        <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.5.6\">\n            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n            <PrivateAssets>all</PrivateAssets>\n        </PackageReference>\n        <PackageReference Include=\"coverlet.collector\" Version=\"6.0.0\">\n            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n            <PrivateAssets>all</PrivateAssets>\n        </PackageReference>\n\n        <PackageReference Include=\"FluentAssertions\">\n            <Version>6.12.0</Version>\n        </PackageReference>\n        <PackageReference Include=\"FluentAssertions.Analyzers\">\n            <Version>0.29.1</Version>\n            <PrivateAssets>all</PrivateAssets>\n            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n        </PackageReference>\n        <PackageReference Include=\"FluentAssertions.ArgumentMatchers.Moq\">\n            <Version>3.1.0</Version>\n        </PackageReference>\n\n        <PackageReference Include=\"JetBrains.Annotations\">\n            <Version>2023.3.0</Version>\n        </PackageReference>\n        <PackageReference Include=\"LightInject\">\n            <Version>6.6.4</Version>\n        </PackageReference>\n        <PackageReference Include=\"Microsoft.NET.Test.Sdk\">\n            <Version>17.8.0</Version>\n        </PackageReference>\n        <PackageReference Include=\"Moq\">\n            <Version>4.20.70</Version>\n        </PackageReference>\n        <PackageReference Include=\"xunit.abstractions\">\n            <Version>2.0.3</Version>\n        </PackageReference>\n        <PackageReference Include=\"xunit.extensibility.core\">\n            <Version>2.6.6</Version>\n        </PackageReference>\n        <PackageReference Include=\"xunit.runner.console\">\n            <Version>2.6.6</Version>\n            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n            <PrivateAssets>all</PrivateAssets>\n        </PackageReference>\n        <PackageReference Include=\"xunit.analyzers\">\n            <Version>1.10.0</Version>\n        </PackageReference>\n        <PackageReference Include=\"xunit.assert\">\n            <Version>2.6.6</Version>\n        </PackageReference>\n\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\..\\..\\Diz.Test\\Diz.Test.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers.Test/Diz.Controllers.Test/src/ImportRomDialogontroller.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model.project;\nusing Diz.Core.serialization;\nusing Diz.Core.util;\nusing Diz.Cpu._65816.import;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing LightInject;\nusing Moq;\nusing Xunit;\n\nnamespace Diz.Controllers.Test;\n\npublic class ImportRomDialogControllerTest : ContainerFixture\n{\n    [Inject] private readonly IImportRomDialogController importRomDialogController = null!;\n    [Inject] private readonly ISampleRomTestData sampleDataFixture = null!;\n    \n    public event EventHandler? SimulateViewActions;\n    private const string RomFilename = \"SAMPLEROM\";\n    private ImportRomSettings? generatedSettings;\n    private Mock<IImportRomDialogView>? mockView = null!;\n\n    protected override void Configure(IServiceRegistry serviceRegistry)\n    {\n        base.Configure(serviceRegistry);\n\n        serviceRegistry.Register<ISampleRomTestData, SampleRomTestDataFixture>(new PerContainerLifetime());\n\n        serviceRegistry.Register<IReadFromFileBytes>(factory =>\n        {\n            var mockLinkedRomBytesProvider = TestUtil.CreateReadFromFileMock(\n                factory.GetInstance<ISampleRomTestData>().SampleRomBytes\n            );\n            return mockLinkedRomBytesProvider.Object;\n        });\n\n        serviceRegistry.Register(factory => new Mock<ICommonGui>().Object);\n\n        serviceRegistry.Register(factory =>\n        {\n            mockView = new Mock<IImportRomDialogView>();\n            mockView.Setup(x => x.ShowAndWaitForUserToConfirmSettings())\n                .Callback(\n                    () =>\n                    {\n                        SimulateViewActions?.Invoke(null, EventArgs.Empty);\n                        importRomDialogController.Submit();\n                    }).Returns(true);\n\n            mockView.SetupGet(x => x.EnabledVectorTableEntries).Returns(new List<string>());\n\n            return mockView.Object;\n        });\n    }\n\n    private void Run(Action? uiActions = null)\n    {\n        if (uiActions != null)\n            SimulateViewActions += (sender, args) => uiActions();\n        \n        generatedSettings = importRomDialogController.PromptUserForImportOptions(RomFilename);\n    }\n\n    [Fact]\n    public void Defaults()\n    {\n        Run();\n        generatedSettings!.RomBytes.Should().BeEquivalentTo(sampleDataFixture.SampleRomBytes);\n        generatedSettings.RomFilename.Should().Be(RomFilename);\n    }\n\n    [Fact]\n    public void WithNoLabels()\n    {\n        Run(() => importRomDialogController.Builder.OptionClearGenerateVectorTableLabels());\n        generatedSettings!.InitialLabels.Should().BeEmpty(\"We cleared them in the UI code\");\n    }\n\n    [Fact]\n    public void WithTwoLabels()\n    {\n        mockView!.SetupGet(x => x.EnabledVectorTableEntries)\n            .Returns([\n                SnesVectorNames.Native_ABORT,\n                SnesVectorNames.Emulation_RESET\n            ]);\n        \n        Run(() =>\n        {\n            // importRomDialogController.Builder.OptionSetGenerateVectorTableLabelFor(SnesVectorNames.Native_ABORT, true);\n            // importRomDialogController.Builder.OptionSetGenerateVectorTableLabelFor(SnesVectorNames.Emulation_RESET, true);\n        });\n\n        var vectorNames = generatedSettings!.InitialLabels.Select(x => x.Value.Name).ToList();\n        vectorNames.Should().HaveCount(2);\n    }\n    \n    public static TheoryData<bool> EnableDisableLabelGeneration =>\n        new()\n        {\n            true, \n            false\n        };\n\n    [Theory, MemberData(nameof(EnableDisableLabelGeneration))]\n    public void LabelGenerationDisable(bool labelGenerationEnabled)\n    {\n        mockView!.SetupGet(x => x.EnabledVectorTableEntries)\n            .Returns(new List<string>\n            {\n                SnesVectorNames.Native_ABORT,\n                SnesVectorNames.Emulation_RESET,\n            });\n\n        Run(() =>\n        {\n            importRomDialogController.Builder.OptionGenerateSelectedVectorTableLabels = labelGenerationEnabled;\n        });\n\n        generatedSettings!.InitialLabels.Should().HaveCount(labelGenerationEnabled ? 2 : 0);\n    }\n\n    [Fact]\n    public void ControllerProperties()\n    {\n        Run();\n        importRomDialogController.CartridgeTitle.Should().Be(sampleDataFixture.Project.InternalRomGameName);\n\n        var input = importRomDialogController.Builder.Input;\n\n        input.Filename.Should().Be(RomFilename);\n        input.RomBytes.Should().HaveCountGreaterThan(100);\n        input.RomSettingsOffset!.Value.Should().Be(RomUtil.LoromSettingOffset);\n        \n        var snesRomAnalysisResults = input.AnalysisResults!;\n        snesRomAnalysisResults.RomMapMode.Should().Be(RomMapMode.LoRom);\n        snesRomAnalysisResults.DetectedRomMapModeCorrectly.Should().Be(true);\n        snesRomAnalysisResults.RomSpeed.Should().Be(sampleDataFixture.Project.Data.RomSpeed);\n    }\n}"
  },
  {
    "path": "Diz.Controllers/Diz.Controllers.Test/Diz.Controllers.Test/src/LogCreatorSettingsEditorControllerTests.cs",
    "content": "﻿#nullable enable\n\nusing System;\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.export;\nusing Diz.Core.util;\nusing Diz.LogWriter.util;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing Moq;\nusing Xunit;\n\nnamespace Diz.Controllers.Test;\n\npublic class LogCreatorSettingsEditorControllerTests : ContainerFixture\n{\n    private static IFilesystemService CreateFilesystemMockObject()\n    {\n        var fsMock = new Mock<IFilesystemService>();\n\n        fsMock.Setup(x => x.DirectoryExists(It.IsAny<string>())).Returns(true);\n        fsMock.Setup(x => x.CreateDirectory(It.IsAny<string>()));\n\n        return fsMock.Object;\n    }\n\n    private static ILogCreatorSettingsEditorView CreateLogCreatorSettingsEditorView()\n    {\n        var viewMock = new Mock<ILogCreatorSettingsEditorView>();\n        viewMock.Setup(x => x.PromptEditAndConfirmSettings()).Returns(true);\n        return viewMock.Object;\n    }\n\n    [Inject] private readonly Func<LogWriterSettings, ISampleAssemblyTextGenerator> createSampleTextFn = null!;\n\n    [Fact]\n    public void Basics()\n    {\n        var fsMock = CreateFilesystemMockObject();\n        var viewMock = CreateLogCreatorSettingsEditorView();\n        var controller = new LogCreatorSettingsEditorController(viewMock, fsMock, createSampleTextFn);\n\n        controller.ValidateExportSettings().Should().BeTrue(\"default settings should be valid\");\n        controller.PromptSetupAndValidateExportSettings().Should().BeTrue(\"dialog unchanged settings should be valid\");\n\n        controller.GetSampleOutput().Should().Contain(\"UNREACH\");\n    }\n\n    [Fact]\n    public void TestSampleTextGeneration()\n    {\n        createSampleTextFn.Should().NotBeNull();\n        var x = createSampleTextFn(new LogWriterSettings());\n    }\n}"
  },
  {
    "path": "Diz.Controllers/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net6.0\": {\n      \"GitInfo\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.2.0, )\",\n        \"resolved\": \"2.2.0\",\n        \"contentHash\": \"SKTmjt40sYqoDB3S7HFVUToUN/vl6Zrt+PuSHdjbQDlcBx0JAZBdS7BXVbVKyy+0iaaF3FBd4ZxspIlKQCC9Lw==\"\n      },\n      \"LightInject\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.4.0, )\",\n        \"resolved\": \"6.4.0\",\n        \"contentHash\": \"WjrY67fnaUJzBSnb7wiaZUOSl7gmxjfg+xo+GrHI55jz0YZR2b5ZB+lDOM5XDu2+2+/OnVT3y4+6BOv6xTqo9w==\"\n      },\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.7.6\",\n        \"contentHash\": \"it17cdP9hzppmvb7Ue3tbXBKWC8ZtcbM7Uz/svRij8HoMwHAPibXEX5lOaavWmyVV6t6ATcPuE0+K6LxUAWoxA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.3.5\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"1.7.1\",\n          \"System.Interactive\": \"4.1.1\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.7.1\",\n          \"System.ValueTuple\": \"4.5.0\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"10.3.6\",\n        \"contentHash\": \"iMd370ZDx6ydm8t7bIFdRbSKX0e42lpvCtifUSbTSXOk5iKjmgl7HU0PXBhIWQAyIRi3gCwfMI9luj8H6K+byw==\"\n      },\n      \"IX.Observable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.7.3\",\n        \"contentHash\": \"YiOcqYWatb335Jo1lSu6kFd+DFJlc1TZPiUr4o4BtnfBb061YGMfhkAshZCfHL6AWgORWfGYH/kQh7T+jxBUiA==\",\n        \"dependencies\": {\n          \"IX.StandardExtensions\": \"[0.7.3, 0.8.0)\",\n          \"JetBrains.Annotations\": \"2020.1.0\"\n        }\n      },\n      \"IX.StandardExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.7.3\",\n        \"contentHash\": \"vNDln18OhH5UFThM4kGYLExGQaP0Fu6YtbCMTmHymOBLOjurmmoJEkIm7gRxjPo1F1itOglofuM2LdM5+kMvsw==\",\n        \"dependencies\": {\n          \"JetBrains.Annotations\": \"2020.1.0\",\n          \"System.ComponentModel.Annotations\": \"4.7.0\"\n        }\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2021.3.0\",\n        \"contentHash\": \"Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==\"\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.8\",\n        \"contentHash\": \"a/zkJHzyzjIilu5cn5SnnCkPDbfUD+nLBpCaSivp0GCotBR6w7S8CaDymI5p0qFB1XUImgii1AqWYZsIK+Lh5g==\"\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.2.0\",\n        \"contentHash\": \"WrnwHktJbwW2A4hhW8TwLPJeu+F0Gl8QCmHs8bm+OEggrPk1Rv50lDYH/+8HNtsCOFJcxmbNJ5XzZCYe0EkRrQ==\",\n        \"dependencies\": {\n          \"JetBrains.Profiler.Api\": \"1.1.8\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.3.3\",\n        \"contentHash\": \"N8+hwhsKZm25tDJfWpBSW7EGhH/R7EMuiX+KJ4C4u+fCWVc1lJ5zg1u3S1RPPVYgTqhx/C3hxrqUpi6RwK5+Tg==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.7.1\",\n        \"contentHash\": \"B43Zsz5EfMwyEbnObwRxW5u85fzJma3lrDeGcSAV1qkhSRTNY5uXAByTn9h9ddNdhM+4/YoLc/CI43umjwIl9Q==\"\n      },\n      \"System.ComponentModel.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"0YFqjhp/mYkDGpU0Ye1GjE53HMp9UVfGN7seGpAMttAC0C40v5gw598jCgpbBLMmCo0E5YRLBv5Z2doypO49ZQ==\"\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Interactive\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.1\",\n        \"contentHash\": \"pL6aZm6Im/KSpQADvJMr3sK5d/Q/iQbC4TQ2zQfiDZ+qzAskuMgrtgnCheAwLX1vC5YDTXpXnVjeMzfxUZzfjQ==\"\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"pBnVzNQYd0OHqh0VLu/hi0zFOTtyF8QwtziQBmzX/ZtVOea4+JEVOGu29DHeSOA0a9SFrYjQorBrOLuKLhcMNQ==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==\"\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"1.0.0\",\n          \"ExtendedXmlSerializer\": \"3.7.6\",\n          \"FluentValidation\": \"10.3.6\",\n          \"IX.Observable\": \"0.7.3\",\n          \"JetBrains.Annotations\": \"2021.3.0\",\n          \"JetBrains.Profiler.SelfApi\": \"2.2.0\",\n          \"LightInject\": \"6.4.0\",\n          \"SharpZipLib\": \"1.3.3\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.Extensions\": \"6.0.0\",\n          \"System.Text.Encoding.CodePages\": \"6.0.0\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"IX.Observable\": \"0.7.3\"\n        }\n      },\n      \"diz.cpu.65816\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"1.0.0\",\n          \"Diz.Core.Interfaces\": \"1.0.0\"\n        }\n      },\n      \"diz.import\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"1.0.0\",\n          \"Diz.Cpu.65816\": \"1.0.0\"\n        }\n      },\n      \"diz.logwriter\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"1.0.0\",\n          \"Diz.Cpu.65816\": \"1.0.0\",\n          \"LightInject\": \"6.4.0\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Core/Diz.Core.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <UseWindowsForms>false</UseWindowsForms>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <RootNamespace>Diz.Core</RootNamespace>\n    <AssemblyName>Diz.Core</AssemblyName>\n    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"ExtendedXmlSerializer\">\n      <Version>3.9.6</Version>\n    </PackageReference>\n    <PackageReference Include=\"JetBrains.Annotations\">\n      <Version>2023.3.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"JetBrains.Profiler.SelfApi\" Version=\"2.5.0\" />\n    <PackageReference Include=\"LightInject\">\n      <Version>6.6.4</Version>\n    </PackageReference>\n    <PackageReference Include=\"SharpZipLib\">\n      <Version>1.4.2</Version>\n    </PackageReference>\n    <PackageReference Include=\"System.Diagnostics.Tracing\">\n      <Version>4.3.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"System.Text.Encoding.CodePages\" Version=\"8.0.0\" />\n    <PackageReference Include=\"FluentValidation\">\n      <Version>11.9.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"System.Resources.Extensions\" Version=\"8.0.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"app.config\" />\n    <None Remove=\"import\\**\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Diz.Core.Interfaces\\Diz.Core.Interfaces.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Remove=\"arch\\CpuOperations.cs\" />\n    <Compile Remove=\"import\\**\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Remove=\"import\\**\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Diz.Core/Interfaces.cs",
    "content": "﻿#nullable enable\n\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\n\nnamespace Diz.Core;\n\npublic interface IDataRange\n{\n    public int MaxCount { get; }\n        \n    public int StartIndex { get; set; }\n    public int EndIndex { get; set; }\n    public int RangeCount { get; set; }\n\n    public void ManualUpdate(int newStartIndex, int newRangeCount);\n}\n\npublic interface IDataFactory\n{\n    // TODO: eventually, make this be IData. it's a whole new refactor though. \n    Data Create();\n}\n\n// TODO: maybe make this a decorator? for IDataFactory, then get rid of it.\npublic interface ISampleDataFactory : IDataFactory\n{\n\n}\n\n\npublic interface IProjectFileAssemblyExporter\n{\n    bool ExportAssembly(string projectFileName);\n}\n\n\npublic interface IProjectProvider\n{\n    Project? Read();\n}\n\n\npublic interface IProjectFileOpener : IProjectProvider\n{\n    void SetOpenFilename(string projectFilename);\n}\n\npublic static class ProjectFileProviderExtensions\n{\n    public static Project? ReadProjectFromFile(this IProjectFileOpener @this, string filename)\n    {\n        @this.SetOpenFilename(filename);\n        return @this.Read();\n    }\n}\n\npublic interface IProjectFactoryFromRomImportSettings : IProjectProvider\n{\n    \n}"
  },
  {
    "path": "Diz.Core/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.Core.services;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizCoreServicesCompositionRoot))]"
  },
  {
    "path": "Diz.Core/app.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <runtime>\n  </runtime>\n</configuration>"
  },
  {
    "path": "Diz.Core/commands/Commands.cs",
    "content": "﻿namespace Diz.Core.commands;\n\npublic class MarkCommand\n{\n    public enum MarkManyProperty\n    {\n        Flag = 0,\n        DataBank = 1,\n        DirectPage = 2,\n        MFlag = 3,\n        XFlag = 4,\n        CpuArch = 5,\n    };\n        \n    public MarkManyProperty Property { get; set; }\n    public int Start { get; set; }\n    public int Count { get; set; }\n    public object Value { get; set; }\n}"
  },
  {
    "path": "Diz.Core/datasubset/DataSubset.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.datasubset\n{\n    public interface IDataSubsetLoader<TRow, TItem>\n    {\n        // provide a row (either retrieve from cache or make a new one, either way)\n        TRow RowValueNeeded(int largeOffset, DataSubset<TRow, TItem> subset);\n        \n        void OnBigWindowChangeStart(DataSubset<TRow, TItem> subset);\n        void OnBigWindowChangeFinished(DataSubset<TRow, TItem> subset);\n    }\n\n    public class DataSubset<TRow, TItem> : INotifyPropertyChangedExt\n    {\n        // the full range of items to pick from.\n        // anything that deals with \"largeIndex\" refers to an index into THIS list.\n        //\n        // note: client may filter or sort this list ahead of time, it doesn't have to be 1:1 with the underlying data\n        public List<TItem> Items\n        {\n            get => items;\n            set\n            {\n                DropRowCache();\n                this.SetField(PropertyChanged, ref items, value);\n            }\n        }\n\n        public IDataSubsetLoader<TRow, TItem> RowLoader { get; init; }\n\n        // rows (relative)\n        public int StartingRowLargeIndex\n        {\n            get => startingRowLargeIndex;\n            set\n            {\n                if (Items == null)\n                    throw new ArgumentException(\"RomBytes must be set before setting view dimensions\");\n                \n                if (!IsValidLargeOffset(value))\n                    throw new ArgumentException(\"StartingRowLargeIndex is out of range\");\n\n                // validate window range is OK.\n                if (value + RowCount > Items.Count)\n                    throw new ArgumentException(\"Window size is out of range\");\n                \n                UpdateDimensions(RowCount, value, \n                    () => this.SetField(PropertyChanged, ref startingRowLargeIndex, value));\n            }\n        }\n\n        // zero is OK.\n        public int RowCount\n        {\n            get => rowCount;\n            set\n            {\n                if (value < 0) \n                    throw new ArgumentOutOfRangeException(nameof(RowCount));\n                \n                if (Items == null)\n                    throw new ArgumentException(\"RomBytes must be set before setting view dimensions\");\n\n                if (value != 0 && !IsValidLargeOffset(value - 1))\n                    throw new ArgumentException(\"Count out of range\");\n\n                // validate window range is OK.\n                if (!IsValidLargeOffset(StartingRowLargeIndex))\n                    throw new ArgumentException(\"starting large index is out of range\");\n                        \n                if (StartingRowLargeIndex + value > Items.Count)\n                {\n                    EndingRowLargeIndex = Items.Count - 1;\n                }\n\n                UpdateDimensions(value, StartingRowLargeIndex, \n                    () => this.SetField(PropertyChanged, ref rowCount, value));\n            }\n        }\n\n        protected virtual void UpdateDimensions(int newRowCount, int newStartingRowLargeIndex, Action updateAction)\n        {\n            if (newRowCount != RowCount || newStartingRowLargeIndex != StartingRowLargeIndex)\n                OnWindowDimensionsChanging(newStartingRowLargeIndex, newRowCount);\n\n            updateAction();\n        }\n\n        // called right before we change StartingRowLargeIndex and RowCount\n        [SuppressMessage(\"ReSharper\", \"UnusedParameter.Local\")]\n        private void OnWindowDimensionsChanging(int newRowStartingIndex, int newRowCount)\n        {\n            DropRowCache();\n        }\n        \n        public int EndingRowLargeIndex\n        {\n            get => StartingRowLargeIndex + RowCount - 1;\n            set => StartingRowLargeIndex = value - RowCount + 1;\n        }\n        \n        // main idea here is, this list never changes until we scroll, in which case we drop and re-add\n        // everything.  recalculating this list should never do anything that involves a lot of processing.\n        // instead, we'll leave the heavy lifting to cachedRows, which can do fancier things if needed\n        // like predict which rows might be needed later.\n        public List<TRow> OutputRows\n        {\n            get\n            {\n                if (outputRows != null)\n                    return outputRows;\n\n                DropRowCache();\n                CacheRows();\n\n                return outputRows;\n            }\n        }\n\n        private int startingRowLargeIndex;\n        private int rowCount;\n        private List<TRow> outputRows;\n        private List<TItem> items;\n\n        // this only ever needs to happen when the dimensions change\n        // if startingIndex and count don't change, this doesn't need to be recalculated.\n        private void CacheRows()\n        {\n            Debug.Assert(outputRows == null);\n            \n            outputRows = new List<TRow>(RowCount);\n            \n            RowLoader.OnBigWindowChangeStart(this);\n            for (var i = StartingRowLargeIndex; i < StartingRowLargeIndex + RowCount; ++i)\n            {\n                var newRow = RowValueNeededForLargeOffset(i);\n                outputRows.Add(newRow);\n            }\n            SetNotifyChangedForAllRows(register: true);\n            RowLoader.OnBigWindowChangeFinished(this);\n        }\n\n        private void DropRowCache()\n        {\n            if (outputRows == null)\n                return;\n\n            SetNotifyChangedForAllRows(register: false);\n\n            outputRows = null;\n        }\n\n        private void SetNotifyChangedForAllRows(bool register)\n        {\n            foreach (var row in outputRows)\n            {\n                if (!(row is INotifyPropertyChanged iNotify))\n                    continue;\n\n                if (register)\n                    iNotify.PropertyChanged += OnRowPropertyChanged;\n                else\n                    iNotify.PropertyChanged -= OnRowPropertyChanged;\n            }\n        }\n\n        private void OnRowPropertyChanged(object sender, PropertyChangedEventArgs e)\n        {\n            // underlying data in one of the visible rows just changed, so pass that along so\n            // listeners can get a notification that they should refresh the data.\n            \n            PropertyChanged?.Invoke(sender, e);\n        }\n\n        // key thing: this class (DataSubset) will itself cache the current outputRows\n        // which don't need to change as long as the view doesn't change.\n        //\n        // RowLoader's job is:\n        // - must cache all visible rows\n        // - optionally, selectively cache some rows no longer in view anymore\n        //\n        // the goal is: for small amounts of scrolling, make sure repopulating outputRows\n        // is a quick operation. this will be true if RowLoader does a good job saving recently\n        // cached rows and predicting which ones might be needed soon.\n        //\n        // this also keeps the complex caching logic can stay out of this class and in RowLoader.\n        //\n        // example: if a user is looking at 10 rows in the middle of a 100 count data source,\n        // the screen GUI only need 10 row objects to exist.  however, RowLoader might choose to also cache\n        // an extra +/- 25 most recently used and rows that might be probably used in the near future.\n        // so if the user is scrolling around in the same area, they might hit some of the non-visible cache\n        // when rows are needed. that will speed up the GUI operations.\n        //\n        // if needed, in the future, predictive row caching could be done on a background thread as well.\n        protected TRow RowValueNeededForLargeOffset(int largeOffset) =>\n            RowLoader.RowValueNeeded(largeOffset, this);\n\n        public bool IsRowOffsetValid(int rowOffset) =>\n            rowOffset >= 0 && rowOffset < RowCount;\n\n        public bool IsLargeOffsetContainedInVisibleRows(int largeOffset) =>\n            largeOffset >= startingRowLargeIndex && largeOffset <= EndingRowLargeIndex;\n\n        public bool IsValidLargeOffset(int largeOffset) =>\n            largeOffset >= 0 && largeOffset < Items?.Count;\n\n        public int GetRowIndexFromLargeOffset(int largeOffset) =>\n            !IsLargeOffsetContainedInVisibleRows(largeOffset)\n                ? -1\n                : largeOffset - startingRowLargeIndex;\n\n        public int GetLargeOffsetFromRowOffset(int rowOffset) =>\n            !IsRowOffsetValid(rowOffset)\n                ? -1\n                : rowOffset + startingRowLargeIndex;\n\n        public event PropertyChangedEventHandler PropertyChanged;\n\n        [NotifyPropertyChangedInvocator]\n        public void OnPropertyChanged([CallerMemberName] string propertyName = null)\n        {\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/datasubset/DataSubsetSupport.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nnamespace Diz.Core.datasubset\n{\n    public abstract class DataSubsetLoader<TRow, TItem> : IDataSubsetLoader<TRow, TItem>\n    {\n        public class Entry\n        {\n            public TRow Row { get; set; }\n            public int AgeScore { get; set; } // 0 = newer, higher = older\n        }\n\n        // map large data index offset to a row\n        // \n        // sometimes this will contain extra or not enough rows, and we'll page them in and out as needed.\n        // this dictionary ALWAYS includes all of the currently displayed rows,\n        // but also, can include more cached rows that we can kick out as needed to save memory.\n        private readonly Dictionary<int, Entry> cachedRows = new();\n\n        // tune as needed. if user can see about 20 rows at a time, we'll keep around 10x that in memory.\n        // if cached rows are in memory, it'll make small scrolling (like bouncing around near the same\n        // couple of rows) already cached\n        //\n        // this can be jacked WAY up with little effect except using more memory.\n        // hike it if you need more perf.\n        public int TargetCachedMultiplier { get; init; } = 15;\n        \n        public int TargetCachedRows { get; protected set; }\n\n        // we'll allow going a certain percentage over the target before cleaning up.\n        // that way we're only cleaning up in chunks and not in individual rows.\n        public int FuzzThreshold => (int)(TargetCachedRows / (float)TargetCachedMultiplier);\n\n        public virtual void OnBigWindowChangeStart(DataSubset<TRow, TItem> subset)\n        {\n            TargetCachedRows = subset.RowCount * TargetCachedMultiplier;\n            \n            IncrementAllAgeScores();\n        }\n\n        private void IncrementAllAgeScores()\n        {\n            foreach (var entry in cachedRows)\n                entry.Value.AgeScore++;\n        }\n\n        public virtual TRow RowValueNeeded(int largeOffset, DataSubset<TRow, TItem> subset)\n        {\n            var entry = GetOrCreateRowEntry(largeOffset, subset);\n            entry.AgeScore = 0; // any recent rows will always be aged at zero\n            return entry.Row;\n        }\n\n        private Entry GetOrCreateRowEntry(int largeIndex, DataSubset<TRow, TItem> subset)\n        {\n            if (cachedRows.TryGetValue(largeIndex, out var entry))\n                return entry;\n\n            // assume this creation is expensive, we're optimizing to minimize # initializations here\n            entry = new Entry\n            {\n                Row = CreateNewRow(subset, largeIndex),\n            };\n\n            cachedRows[largeIndex] = entry;\n            return entry;\n        }\n\n        protected abstract TRow CreateNewRow(DataSubset<TRow, TItem> subset, int largeIndex);\n\n        // this is a hint that big changes just finished up (like recreating the rows due to a scroll),\n        // so it's likely a good time to kick irrelevant rows out of the cache.\n        //\n        // we could do a bunch of clever stuff, I'm just going to a really simple age check\n        // and kick out the oldest rows (rows that haven't been in any view for a while)\n        // which are furthest away from the current window\n        public virtual void OnBigWindowChangeFinished(DataSubset<TRow, TItem> subset)\n        {\n            // see if we're about 10% over our target, and if so, dump about 10% of the cache.\n            // it's OK to go over so that we're not constantly dumping cache with every small change.\n            if (cachedRows.Count <= TargetCachedRows + FuzzThreshold)\n                return;\n            \n            // we're over our target, so start dropping the oldest least useful stuff from the cache\n            var oldestDeletionCandidates = (from kvp in cachedRows\n                where kvp.Value.AgeScore != 0\n                orderby kvp.Value.AgeScore descending\n                select kvp).Take(FuzzThreshold).ToList();\n\n            foreach (var entry in oldestDeletionCandidates)\n            {\n                cachedRows.Remove(entry.Key);\n            }\n        }\n    }\n\n    public class DataSubsetSimpleLoader<TRow, TItem> : IDataSubsetLoader<TRow, TItem> where TRow : new()\n    {\n        // no caching, just create a new row as needed each time\n        // if performance is an issue, use another strategy.\n        public TRow RowValueNeeded(int largeOffset, DataSubset<TRow, TItem> subset)\n        {\n            TRow newRow = new();\n\n            PopulateRow?.Invoke(ref newRow, largeOffset);\n\n            return newRow;\n        }\n        \n        public delegate void PopulateNewlyCreatedRow(ref TRow newlyCreatedRow, int largeIndex);\n\n        public PopulateNewlyCreatedRow PopulateRow { get; set; }\n\n        public void OnBigWindowChangeStart(DataSubset<TRow, TItem> subset) {}\n        public void OnBigWindowChangeFinished(DataSubset<TRow, TItem> subset) {}\n    }\n}"
  },
  {
    "path": "Diz.Core/datasubset/DataSubsetWithSelection.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing Diz.Core.util;\n\nnamespace Diz.Core.datasubset\n{\n    // controls what rows are visible and scrolls intelligently based on our offset\n    public class DataSubsetWithSelection<TRow, TItem> : DataSubset<TRow, TItem>\n    {\n        public TRow SelectedRow =>\n            RowValueNeededForLargeOffset(SelectedLargeIndex);\n        \n        public int SelectedRowIndex => \n            GetRowIndexFromLargeOffset(SelectedLargeIndex);\n\n        // when set: when the start or end range changes, the selected row will be\n        // clamped to be within the Start..End range.\n        public bool WindowResizeKeepsSelectionInRange\n        {\n            get => windowResizeKeepsSelectionInRange;\n            set\n            {\n                this.SetField(ref windowResizeKeepsSelectionInRange, value);\n                ClampSelectionIfNeeded();\n            }\n        }\n        private bool windowResizeKeepsSelectionInRange;\n\n        // when set: when the selection is changed, the start and end points will move\n        // to keep the selection inside the range.\n        public bool EnsureBoundariesEncompassWhenSelectionChanges\n        {\n            get => ensureBoundariesEncompassWhenSelectionChanges;\n            set\n            {\n                this.SetField(ref ensureBoundariesEncompassWhenSelectionChanges, value);\n                EnsureViewContainsLargeIndex(SelectedLargeIndex);\n            }\n        }\n        private bool ensureBoundariesEncompassWhenSelectionChanges = true;\n        \n        private void EnsureViewContainsLargeIndex(int largeIndex)\n        {\n            if (RowCount == 0)\n                return;\n\n            Debug.Assert(IsValidLargeOffset(largeIndex));\n\n            if (largeIndex < StartingRowLargeIndex)\n            {\n                StartingRowLargeIndex = largeIndex;\n            } \n            else if (largeIndex > EndingRowLargeIndex)\n            {\n                EndingRowLargeIndex = largeIndex;\n            }\n        }\n\n        public int SelectedLargeIndex\n        {\n            get => selectedLargeIndex;\n            set\n            {\n                if (!IsValidLargeOffset(value))\n                    throw new ArgumentException(\"Invalid large value\");\n\n                // var clampedValue = GetClampedIndexIfNeeded(value);\n\n                if (NotifyPropertyChangedExtensions.FieldIsEqual(selectedLargeIndex, value))\n                    return;\n\n                selectedLargeIndex = value;\n\n                EnsureViewContainsSelectionIfNeeded();\n            \n                OnPropertyChanged();\n            }\n        }\n\n        public int LargestPossibleStartingLargeIndex => Items.Count - RowCount;\n\n        private int selectedLargeIndex;\n\n\n        private void EnsureViewContainsSelectionIfNeeded()\n        {\n            if (ensureBoundariesEncompassWhenSelectionChanges)\n                EnsureViewContainsLargeIndex(SelectedLargeIndex);\n        }\n\n        private void ClampSelectionIfNeeded() => \n            SelectedLargeIndex = GetClampedIndexIfNeeded(SelectedLargeIndex);\n        \n        private int GetClampedIndexIfNeeded(int largeIndex) =>\n            !windowResizeKeepsSelectionInRange \n                ? largeIndex\n                : GetLargeIndexClampedToVisibleRows(largeIndex);\n\n        public int GetLargeIndexClampedToVisibleRows(int largeIndexToClamp) => \n            Util.Clamp(largeIndexToClamp, StartingRowLargeIndex, EndingRowLargeIndex);\n\n        public void SelectRow(int rowIndex) => \n            SelectedLargeIndex = GetLargeOffsetFromRowOffset(rowIndex);\n\n        protected override void UpdateDimensions(int newRowCount, int newStartingRowLargeIndex, Action updateAction)\n        {\n            base.UpdateDimensions(newRowCount, newStartingRowLargeIndex, updateAction);\n            ClampSelectionIfNeeded();\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/export/LogWriterSettings.cs",
    "content": "﻿#nullable enable\n\nusing System.IO;\nusing System.Xml.Serialization;\nusing Diz.Core.util;\n\n/*\n * TODO:\n * Couple things for ongoing refactors:\n * 1) This class should ideally live with the Diz.LogWriter project (not Diz.Core)\n * 2) Probably use dependency injection starting with this system to register settings providers in Diz like this one?\n */\n\nnamespace Diz.Core.export;\n\n\npublic interface ILogWriterSettings\n{\n}\n\npublic record LogWriterSettings : ILogWriterSettings\n{\n    // path to output file or folder\n    public const string DefaultStr = \"%label:-22% %code:37%;%pc%|%bytes%|%ia%; %comment%\";\n        \n    public enum FormatUnlabeled\n    {\n        ShowAll = 0,\n        ShowInPoints = 1, // TODO Add Show In Points with +/- labels\n        ShowNone = 2\n    }\n\n    public enum FormatStructure\n    {\n        SingleFile = 0,\n        OneBankPerFile = 1\n    }\n\n    public string Format { get; init; } = DefaultStr;\n    public int DataPerLine { get; init; } = 8;\n    public FormatUnlabeled Unlabeled { get; init; } = FormatUnlabeled.ShowInPoints;\n    public FormatStructure Structure { get; init; } = FormatStructure.OneBankPerFile;\n    \n    // tmp hack until we fix single file mode. allows sample data to still be generated\n    [XmlIgnore] public bool SuppressSingleFileModeDisabledError { get; init; } = false;\n    \n    public bool NewLine { get; init; } = false;\n    public bool OutputExtraWhitespace  { get; init; } = true;\n    public bool GenerateFullLine { get; init; } = true;\n    public bool IncludeUnusedLabels  { get; init; }\n    public bool PrintLabelSpecificComments { get; init; }\n    public bool GeneratePlusMinusLabels { get; init; } = true;\n\n    // this is an experimental option, if useful, remove [XmlIgnore] and add the UI for this\n    [XmlIgnore] public bool AppendFlagTypeToComment { get; init; } = false;\n\n        \n    /// <summary>\n    /// specify an override for the # of bytes to assemble. default is to visit every byte in the entire ROM \n    /// </summary>\n    public int RomSizeOverride { get; init; } = -1;\n\n    /// <summary>\n    /// The (usually absolute) base path to the project directory, if any.\n    /// Don't save this with the project XML.\n    /// </summary>\n    [XmlIgnore]\n    public string? BaseOutputPath { get; init; }\n        \n    /// <summary>\n    /// Relative path to add on after the base path.\n    /// </summary>\n    public string FileOrFolderOutPath { get; init; } = \"export\\\\\";\n\n    public bool OutputToString { get; init; }\n    public string ErrorFilename { get; init; } = \"errors.txt\";\n\n    public LogWriterSettings WithPathRelativeTo(string newFileNameAndPath, string? pathToMakeRelativeTo) =>\n        this with\n        {\n            FileOrFolderOutPath = Util.TryGetRelativePath(newFileNameAndPath, pathToMakeRelativeTo),\n            BaseOutputPath = pathToMakeRelativeTo,\n        };\n\n    public string BuildFullOutputPath()\n    {\n        // this is still a bit of an in-progress mess. sigh.\n        \n        var path = FileOrFolderOutPath;\n        if (Structure == FormatStructure.OneBankPerFile)\n            path += \"\\\\\"; // force it to treat it as a path.\n\n        // if it's absolute path, use that first, ignore base path\n        if (Path.IsPathFullyQualified(path))\n            return path;\n\n        // if it's not an absolute path, combine BaseOutputPath and FileOrFolderPath to get the final\n        var relativeFolderPath = Path.GetDirectoryName(path) ?? \"\";\n        \n        if (Structure == FormatStructure.OneBankPerFile)\n            relativeFolderPath += \"\\\\\"; // force it to treat it as a path.\n\n        return Path.Combine(BaseOutputPath ?? \"\", relativeFolderPath);\n    }\n    \n    public string? Validate(IFilesystemService fs)\n    {\n        var results = new LogWriterSettingsValidator(fs).Validate(this);\n        return !results.IsValid ? results.ToString() : null;\n    }\n\n    public bool IsValid(IFilesystemService fs) => Validate(fs) == null;\n}"
  },
  {
    "path": "Diz.Core/export/LogWriterSettingsValidator.cs",
    "content": "﻿using System.IO;\nusing Diz.Core.util;\nusing FluentValidation;\n\nnamespace Diz.Core.export\n{\n    public class LogWriterSettingsValidator : AbstractValidator<LogWriterSettings>\n    {\n        public LogWriterSettingsValidator(IFilesystemService fs)\n        {\n            When(x => x.OutputToString, () =>\n                    Include(new LogWriterSettingsOutputString()))\n                .Otherwise(() =>\n                    Include(new LogWriterSettingsOutputMultipleFiles(fs)));\n        }\n    }\n\n    public class LogWriterSettingsOutputString : AbstractValidator<LogWriterSettings>\n    {\n        public LogWriterSettingsOutputString()\n        {\n            // runs when OutputToString == true\n            // i.e. when we expect the output to be a single .asm file, and not a directory\n\n            RuleFor(x => x.Structure)\n                .NotEqual(LogWriterSettings.FormatStructure.OneBankPerFile)\n                .WithMessage(\"Can't use one-bank-per-file output with string output enabled\");\n\n            RuleFor(x => x.FileOrFolderOutPath)\n                .Empty()\n                .WithMessage(\"Can't use one-bank-per-file output with valid file or path specified\");\n        }\n    }\n    \n    public class LogWriterSettingsOutputMultipleFiles : AbstractValidator<LogWriterSettings>\n    {\n        private readonly IFilesystemService fs;\n        \n        private bool OutputDirReallyExistsOnDisk(LogWriterSettings settings)\n        {\n            var path = settings.BuildFullOutputPath();\n            return fs.DirectoryExists(Path.GetDirectoryName(path));\n        }\n\n        // this is not the most bulletproof thing in the world.\n        // it's hard to validate without hitting the disk, you should follow this with additional checks\n        private bool PathLooksLikeDirectoryNameOnly(string fileOrFolderPath) =>\n            Path.GetFileName(fileOrFolderPath) == string.Empty ||\n            !Path.HasExtension(fileOrFolderPath);\n        \n        public LogWriterSettingsOutputMultipleFiles(IFilesystemService fs)\n        {\n            // runs when OutputToString == false\n            // i.e. we expect the output path to be a directory path and not a file.\n            \n            this.fs = fs;\n            \n            RuleFor(x => x.FileOrFolderOutPath)\n                .NotEmpty().WithMessage(\"Disassembly output file directory is empty, but is required\");\n                \n            RuleFor(settings => settings)\n                .Must(OutputDirReallyExistsOnDisk).WithMessage(\"Disassembly output directory doesn't exist on disk.\");\n\n            // verify what we have appears to be a filename and NOT a directory\n            RuleFor(x => x.FileOrFolderOutPath)\n                .Must(PathLooksLikeDirectoryNameOnly)\n                .When(settings => settings.Structure == LogWriterSettings.FormatStructure.OneBankPerFile)\n                .WithMessage(\"Disassembly output directory doesn't appear to be a valid directory name\");\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/model/Annotation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing Diz.Core.Interfaces;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\n#if DIZ_3_BRANCH\nusing Diz.Core.model.byteSources;\n#endif\n\nnamespace Diz.Core.model\n{\n    \n    public abstract class Annotation : AnnotationBase\n    #if !DIZ_3_BRANCH\n    {}\n    #else\n        , IParentAware<ByteEntry>\n    {\n        public ByteEntry Parent { get; protected set; }\n        public void OnParentChanged(ByteEntry parent)\n        {\n            Parent = parent;\n        }\n    } \n    #endif\n    \n    public abstract class AnnotationBase : INotifyPropertyChangedExt\n    {\n        public event PropertyChangedEventHandler PropertyChanged;\n\n        [NotifyPropertyChangedInvocator]\n        public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)\n        {\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n        }\n    }\n\n    public class MarkAnnotation : Annotation, IComparable<MarkAnnotation>, IComparable\n    {\n        public FlagType TypeFlag\n        {\n            get => typeFlag;\n            set => this.SetField(ref typeFlag, value);\n        }\n        \n        private FlagType typeFlag = FlagType.Unreached;\n\n        protected bool Equals(MarkAnnotation other)\n        {\n            return typeFlag == other.typeFlag;\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != GetType()) return false;\n            return Equals((MarkAnnotation) obj);\n        }\n        \n        public override int GetHashCode()\n        {\n            return (int) typeFlag;\n        }\n        \n        public int CompareTo(MarkAnnotation other)\n        {\n            if (ReferenceEquals(this, other)) return 0;\n            if (ReferenceEquals(null, other)) return 1;\n            return typeFlag.CompareTo(other.typeFlag);\n        }\n\n        public int CompareTo(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return 1;\n            if (ReferenceEquals(this, obj)) return 0;\n            return obj is MarkAnnotation other ? CompareTo(other) : throw new ArgumentException($\"Object must be of type {nameof(MarkAnnotation)}\");\n        }\n    }\n\n    public class ByteAnnotation : Annotation, IComparable<ByteAnnotation>, IComparable\n    {\n        public byte Val\n        {\n            get => dataByte;\n            set => this.SetField(ref dataByte, value);\n        }\n        \n        private byte dataByte;\n\n        #region Generated Comparison\n        public int CompareTo(ByteAnnotation other)\n        {\n            if (ReferenceEquals(this, other)) return 0;\n            if (ReferenceEquals(null, other)) return 1;\n            return dataByte.CompareTo(other.dataByte);\n        }\n\n        public int CompareTo(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return 1;\n            if (ReferenceEquals(this, obj)) return 0;\n            return obj is ByteAnnotation other ? CompareTo(other) : throw new ArgumentException($\"Object must be of type {nameof(ByteAnnotation)}\");\n        }\n\n        protected bool Equals(ByteAnnotation other)\n        {\n            return dataByte == other?.dataByte;\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != GetType()) return false;\n            return Equals((ByteAnnotation) obj);\n        }\n\n        public override int GetHashCode()\n        {\n            return HashCode.Combine(base.GetHashCode(), dataByte);\n        }\n        #endregion\n    }\n    \n    public class OpcodeAnnotation : Annotation, IComparable<OpcodeAnnotation>, IComparable\n    {\n        public byte DataBank\n        {\n            get => dataBank;\n            set => this.SetField(ref dataBank, value);\n        }\n\n        public int DirectPage\n        {\n            get => directPage;\n            set => this.SetField(ref directPage, value);\n        }\n\n        public bool XFlag\n        {\n            get => xFlag;\n            set => this.SetField(ref xFlag, value);\n        }\n\n        public bool MFlag\n        {\n            get => mFlag;\n            set => this.SetField(ref mFlag, value);\n        }\n        \n        public Architecture Arch\n        {\n            get => arch;\n            set => this.SetField(ref arch, value);\n        }\n\n        private byte dataBank;\n        private int directPage;\n        private bool xFlag;\n        private bool mFlag;\n\n        private Architecture arch;\n\n        #region Equality\n        protected bool Equals(OpcodeAnnotation other)\n        {\n            return DataBank == other.DataBank && DirectPage == other.DirectPage && XFlag == other.XFlag && MFlag == other.MFlag && Arch == other.Arch;\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            return obj.GetType() == GetType() && Equals((OpcodeAnnotation)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            unchecked\n            {\n                var hashCode = DataBank.GetHashCode();\n                hashCode = (hashCode * 397) ^ DirectPage;\n                hashCode = (hashCode * 397) ^ XFlag.GetHashCode();\n                hashCode = (hashCode * 397) ^ MFlag.GetHashCode();\n                hashCode = (hashCode * 397) ^ (int)Arch;\n                return hashCode;\n            }\n        }\n        public int CompareTo(OpcodeAnnotation other)\n        {\n            if (ReferenceEquals(this, other)) return 0;\n            if (ReferenceEquals(null, other)) return 1;\n            var dataBankComparison = dataBank.CompareTo(other.dataBank);\n            if (dataBankComparison != 0) return dataBankComparison;\n            var directPageComparison = directPage.CompareTo(other.directPage);\n            if (directPageComparison != 0) return directPageComparison;\n            var xFlagComparison = xFlag.CompareTo(other.xFlag);\n            if (xFlagComparison != 0) return xFlagComparison;\n            var mFlagComparison = mFlag.CompareTo(other.mFlag);\n            if (mFlagComparison != 0) return mFlagComparison;\n            return arch.CompareTo(other.arch);\n        }\n\n        public int CompareTo(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return 1;\n            if (ReferenceEquals(this, obj)) return 0;\n            return obj is OpcodeAnnotation other ? CompareTo(other) : throw new ArgumentException($\"Object must be of type {nameof(OpcodeAnnotation)}\");\n        }\n        #endregion\n    } \n    \n    // technically, this computed data can be re-created at any time and we keep it because:\n    // 1) serialize so we don't have to recompute on load\n    // 2) so it's faster when figuring out what to display to the user (vs recomputing on the fly)\n    public class BranchAnnotation : Annotation, IComparable<BranchAnnotation>, IComparable\n    {\n        // never modify fields directly. only go through the public fields\n        // cached mark if it's an in vs out point\n        private InOutPoint point = InOutPoint.None;\n        \n        // cached data\n        public InOutPoint Point\n        {\n            get => point;\n            set => this.SetField(ref point, value);\n        }\n        \n        #region Equality\n        protected bool Equals(BranchAnnotation other)\n        {\n            return Point == other.Point;\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            return obj.GetType() == GetType() && Equals((BranchAnnotation)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            var hashCode = (int)Point;\n            return hashCode;\n        }\n        \n        public int CompareTo(BranchAnnotation other)\n        {\n            if (ReferenceEquals(this, other)) return 0;\n            if (ReferenceEquals(null, other)) return 1;\n            return point.CompareTo(other.point);\n        }\n\n        public int CompareTo(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return 1;\n            if (ReferenceEquals(this, obj)) return 0;\n            return obj is BranchAnnotation other ? CompareTo(other) : throw new ArgumentException($\"Object must be of type {nameof(BranchAnnotation)}\");\n        }\n        #endregion\n    }\n    \n    [Serializable]\n    public class ContextMapping : IContextMapping\n    {\n        private string context = \"\";\n        private string nameOverride = \"\";\n\n        public string Context\n        {\n            get => context;\n            set { context = value; OnPropertyChanged(); }\n        }\n\n        public string NameOverride\n        {\n            get => nameOverride;\n            set { nameOverride = value; OnPropertyChanged(); }\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n    \n        protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)\n        {\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n        }\n    }\n\n    // represent a label at a particular SNES address\n    //\n    // Comments here are for the LABEL itself, and not so much about where they're used.\n    // i.e. a label for 0x7E0020 might store a character's HP in RAM. It would look like:\n    // - address: snes address 0x7E0020 (i.e. mapped to a WRAM address)\n    // - label:   \"character_3_hp\"\n    // - comment: \"this address is only used in RAM during battle sequences\"\n    public class Label : Annotation, IAnnotationLabel, IComparable<Label>, IComparable\n    {\n        private string comment = \"\";\n        private string name = \"\";\n\n        public ObservableCollection<IContextMapping> ContextMappings { get; set; } = [];\n        IEnumerable<IReadOnlyContextMapping> IReadOnlyLabel.ContextMappings => ContextMappings;\n        ObservableCollection<IContextMapping> IAnnotationLabel.ContextMappings => ContextMappings;\n\n        public string Name\n        {\n            get => name;\n            set => this.SetField(ref name, value ?? \"\");\n        }\n\n        public string Comment\n        {\n            get => comment;\n            set => this.SetField(ref comment, value ?? \"\");\n        }\n\n        public string GetName(string contextName = \"\")\n        {\n            var mapping = ContextMappings.FirstOrDefault(cm => cm.Context == contextName);\n            var overriddenName = mapping?.NameOverride ?? Name;\n            return string.IsNullOrWhiteSpace(overriddenName) ? Name : overriddenName;\n        }\n\n        #region Equality\n\n        private bool Equals(Label other)\n        {\n            return Name == other.Name && \n                   Comment == other.Comment && \n                   ContextMappings.SequenceEqual(other.ContextMappings, new ContextMappingComparer());\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != GetType()) return false;\n            return Equals((Label)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            unchecked\n            {\n                var hashCode = (Name != null ? Name.GetHashCode() : 0);\n                hashCode = (hashCode * 397) ^ (Comment != null ? Comment.GetHashCode() : 0);\n                hashCode = (hashCode * 397) ^ GetContextMappingsHashCode();\n                return hashCode;\n            }\n        }\n\n        private int GetContextMappingsHashCode()\n        {\n            unchecked\n            {\n                var hash = 0;\n                foreach (var mapping in ContextMappings)\n                {\n                    hash = (hash * 397) ^ (mapping.Context?.GetHashCode() ?? 0);\n                    hash = (hash * 397) ^ (mapping.NameOverride?.GetHashCode() ?? 0);\n                }\n                return hash;\n            }\n        }\n\n        public int CompareTo(Label other)\n        {\n            if (ReferenceEquals(this, other)) return 0;\n            if (ReferenceEquals(null, other)) return 1;\n            \n            var nameComparison = string.Compare(name, other.name, StringComparison.Ordinal);\n            if (nameComparison != 0) return nameComparison;\n            \n            var commentComparison = string.Compare(comment, other.comment, StringComparison.Ordinal);\n            if (commentComparison != 0) return commentComparison;\n            \n            return CompareContextMappings(other);\n        }\n\n        private int CompareContextMappings(Label other)\n        {\n            var thisCount = ContextMappings.Count;\n            var otherCount = other.ContextMappings.Count;\n            \n            var countComparison = thisCount.CompareTo(otherCount);\n            if (countComparison != 0) return countComparison;\n            \n            var thisSorted = ContextMappings.OrderBy(cm => cm.Context).ThenBy(cm => cm.NameOverride).ToList();\n            var otherSorted = other.ContextMappings.OrderBy(cm => cm.Context).ThenBy(cm => cm.NameOverride).ToList();\n            \n            for (int i = 0; i < thisCount; i++)\n            {\n                var contextComparison = string.Compare(thisSorted[i].Context, otherSorted[i].Context, StringComparison.Ordinal);\n                if (contextComparison != 0) return contextComparison;\n                \n                var nameOverrideComparison = string.Compare(thisSorted[i].NameOverride, otherSorted[i].NameOverride, StringComparison.Ordinal);\n                if (nameOverrideComparison != 0) return nameOverrideComparison;\n            }\n            \n            return 0;\n        }\n\n        public int CompareTo(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return 1;\n            if (ReferenceEquals(this, obj)) return 0;\n            return obj is Label other ? CompareTo(other) : throw new ArgumentException($\"Object must be of type {nameof(Label)}\");\n        }\n\n        #endregion\n\n        // Helper class for comparing ContextMapping objects\n        private class ContextMappingComparer : IEqualityComparer<IContextMapping>\n        {\n            public bool Equals(IContextMapping x, IContextMapping y)\n            {\n                if (ReferenceEquals(x, y)) return true;\n                if (x is null || y is null) return false;\n                return x.Context == y.Context && x.NameOverride == y.NameOverride;\n            }\n\n            public int GetHashCode(IContextMapping obj)\n            {\n                unchecked\n                {\n                    return ((obj.Context?.GetHashCode() ?? 0) * 397) ^ (obj.NameOverride?.GetHashCode() ?? 0);\n                }\n            }\n        }\n        \n        public bool IsDefault() => !string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Comment);\n    }\n\n    // used mostly in the assembly text exporting process for generating temp labels that need extra info.\n    // don't serialize or use outside that context.\n    public class TempLabel : Label\n    {\n        [Flags]\n        public enum TempLabelFlags\n        {\n            None = 0,\n            DisallowPlusMinusGeneration = 0x01,\n        }\n\n        public TempLabelFlags Flags { get; set; } = TempLabelFlags.None;\n    }\n    \n    public class Comment : Annotation, IComparable<Comment>, IComparable\n    {\n        private string text = \"\";\n\n        public string Text\n        {\n            get => text;\n            set => this.SetField(ref text, value ?? \"\");\n        }\n\n        #region Equality\n\n        protected bool Equals(Comment other)\n        {\n            return Text == other.Text;\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != GetType()) return false;\n            return Equals((Comment)obj);\n        }\n        public override int GetHashCode()\n        {\n            return Text != null ? Text.GetHashCode() : 0;\n        }\n        \n        public int CompareTo(Comment other)\n        {\n            if (ReferenceEquals(this, other)) return 0;\n            if (ReferenceEquals(null, other)) return 1;\n            return string.Compare(text, other.text, StringComparison.Ordinal);\n        }\n\n        public int CompareTo(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return 1;\n            if (ReferenceEquals(this, obj)) return 0;\n            return obj is Comment other ? CompareTo(other) : throw new ArgumentException($\"Object must be of type {nameof(Comment)}\");\n        }\n\n        #endregion\n    }\n}"
  },
  {
    "path": "Diz.Core/model/ArchProvider.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Diz.Core.Interfaces;\n\nnamespace Diz.Core.model;\n\npublic class DataStoreProvider<T> : IDataStoreProvider<T> where T : class\n{\n    public List<T> Items { get; set; } = new();\n\n    public bool AddIfDoesntExist(T type)\n    {\n        if (Items.Exists(x => x.GetType() == type.GetType()))\n            return false;\n        \n        Items.Add(type);\n        return true;\n    }\n\n    public TSearchFor Get<TSearchFor>() where TSearchFor : class, T\n    {\n        try\n        {\n            return Items.Single(x => x is TSearchFor) as TSearchFor;\n        }\n        catch (InvalidOperationException ex)\n        {\n            throw new InvalidOperationException($\"No API found of type {typeof(T).Name}\", ex);\n        }\n    }\n\n    public IEnumerator<T> GetEnumerator() => Items.GetEnumerator();\n    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)Items).GetEnumerator();\n}"
  },
  {
    "path": "Diz.Core/model/Extensions.cs",
    "content": "﻿using Diz.Core.Interfaces;\n\nnamespace Diz.Core.model\n{\n    public static class DataExtensions\n    {\n        #region UnsafeCompatabilityHelpers\n        \n        // older interface never had to worry about null. new interface now we do. \n        // these are unsafe helper methods for code not yet using the new interface.\n        // new code shouldn't use these if possible, and older code should migrate to checking for null directly.\n        \n        public static byte GetRomByteUnsafe(this IReadOnlyByteSource data, int offset)\n        {\n            // ReSharper disable once PossibleInvalidOperationException\n            return (byte)data.GetRomByte(offset);\n        }\n        \n        public static int GetRomWordUnsafe(this IReadOnlyByteSource data, int offset)\n        {\n            // ReSharper disable once PossibleInvalidOperationException\n            return (int)data.GetRomWord(offset);\n        }\n        \n        public static int GetRomLongUnsafe(this IReadOnlyByteSource data, int offset)\n        {\n            // ReSharper disable once PossibleInvalidOperationException\n            return (int)data.GetRomLong(offset);\n        }\n\n        public static int GetRomDoubleWordUnsafe(this IReadOnlyByteSource data, int offset)\n        {\n            // ReSharper disable once PossibleInvalidOperationException\n            return (int)data.GetRomDoubleWord(offset);\n        }\n        #endregion\n    }\n}"
  },
  {
    "path": "Diz.Core/model/LabelProvider.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.Immutable;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Xml.Serialization;\nusing Diz.Core.Interfaces;\n#if DIZ_3_BRANCH\nusing Diz.Core.model.byteSources;\n#endif\nusing Diz.Core.model.snes;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.model\n{\n    public class LabelsMirroredLabelCacheSearch : IMirroredLabelCacheSearch\n    {\n        private readonly Dictionary<int, IAnnotationLabel> exporterLabelsSubset;\n\n        public LabelsMirroredLabelCacheSearch(IEnumerable<KeyValuePair<int, IAnnotationLabel>> allLabels)\n        {\n            exporterLabelsSubset = allLabels\n                .Where(x=> \n                    // exclude +/- labels\n                    !RomUtil.IsValidPlusMinusLabel(x.Value.Name) &&\n                    \n                    // exclude some auto-generated types of labels\n                    !x.Value.Name.StartsWith(\"UNREACH_\") && \n                    !x.Value.Name.StartsWith(\"CODE_\") && \n                    !x.Value.Name.StartsWith(\"DATA_\") &&\n                    !x.Value.Name.StartsWith(\"DATA8_\") &&\n                    !x.Value.Name.StartsWith(\"DATA16_\") &&\n                    !x.Value.Name.StartsWith(\"DATA24_\") &&\n                    !x.Value.Name.StartsWith(\"LOOSE_OP_\") &&\n                    !x.Value.Name.StartsWith(\"TEXT_\") &&\n                    \n                    // exclude labels that can't possibly be mirrors of any WRAM or other stuff\n                    // (note: keep this in-sync with the checks from AreLabelsSameMirror())\n                    (\n                        RomUtil.GetWramAddressFromSnesAddress(x.Key) != -1 || RomUtil.GetUnmirroredIoRegionFromBank(x.Key) != -1\n                    )\n                    \n                    // probably more are useful to add here...\n                )\n\n                .ToDictionary();\n        }\n\n        public (int labelAddress, IAnnotationLabel labelEntry) SearchOptimizedForMirroredLabel(int snesAddress)\n        {\n            if (exporterLabelsSubset == null)\n                return (-1, null);\n\n            // do this WITHOUT LINQ for optimization purposes\n            // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator\n            foreach (var kvp in exporterLabelsSubset)\n            {\n                if (RomUtil.AreLabelsSameMirror(snesAddress, kvp.Key))\n                {\n                    return (kvp.Key, kvp.Value);\n                }\n            }\n\n            return (-1, null);\n        }\n    }\n    \n    // this system could probably use a redesign.\n    // the entire point of this class is to route all read/writes for Label class\n    // through one point.  then, we can augment the real labels (user-created)\n    // with temporary labels (like temporarily generated during the output assembly code generation).\n    //\n    // when there's no need for assembly labels anymore, we can dump them.\n    //\n    // I think once things are further along, it should be possible to just use a new ByteSource that's overlaid\n    // on top of SnesAddressSpace and add labels to just THAT.\n    public abstract class LabelProviderBase\n    {\n        public abstract IAnnotationLabel GetLabel(int snesAddress);\n        \n        public string GetLabelName(int snesAddress) => \n            GetLabel(snesAddress)?.Name ?? \"\";\n\n        public string GetLabelComment(int snesAddress) => \n            GetLabel(snesAddress)?.Comment ?? \"\";\n    }\n    \n    public class LabelsServiceWithTemp : LabelProviderBase, ILabelServiceWithTempLabels, IEquatable<LabelsServiceWithTemp>\n    {\n        public LabelsServiceWithTemp(Data data)\n        {\n            Data = data;\n            \n            #if DIZ_3_BRANCH\n            NormalProvider = new ByteSourceLabelProvider(data.SnesAddressSpace);\n            #else\n            NormalProvider = new LabelsCollection();\n            #endif\n            \n            TemporaryProvider = new LabelsCollection();\n        }\n        \n        [XmlIgnore] \n        public Data Data { get; }\n\n        private ILabelService NormalProvider { get; }\n        \n        [XmlIgnore] \n        private ILabelService TemporaryProvider { get; }\n        \n        \n        // this isn't bulletproof, but the best we can do for now.\n        // better to replace this with observable collections or something later.\n        public event EventHandler OnLabelChanged;\n\n        // returns both real and temporary labels\n        IEnumerable<KeyValuePair<int, IAnnotationLabel>> IReadOnlyLabelProvider.Labels => Labels;\n\n        public void AddOrReplaceTemporaryLabel(int snesAddress, IAnnotationLabel label)\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            // never generate a label that overrides a real human-generated label that was created manually\n            if (NormalProvider.GetLabel(snesAddress) != null)\n                return;\n            \n            TemporaryProvider.AddLabel(snesAddress, label, true);\n        }\n\n        public void ClearTemporaryLabels()\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            TemporaryProvider.DeleteAllLabels();\n        }\n\n        public void LockLabelsCache()\n        {\n            cachedLabels = ConcatNormalAndTempLabels().ToDictionary();\n            mirroredLabelCacheSearch = new LabelsMirroredLabelCacheSearch(cachedLabels);\n        }\n\n        public void UnlockLabelsCache()\n        {\n            cachedLabels = null;\n            mirroredLabelCacheSearch = null;\n        }\n        \n        // performance only: ALL LABELS: cache of combined temp and real labels together,\n        // so that requests don't have to ask for Concat() which is slow.\n        [CanBeNull] private Dictionary<int, IAnnotationLabel> cachedLabels;\n        // performance only: SUBSET of LABELS: this is mostly used for reducing the search space for\n        // intense label searches for bank mirroring/etc, where complexity is O(N^2) that grows per-label.\n        [CanBeNull] private IMirroredLabelCacheSearch mirroredLabelCacheSearch;\n\n        // very expensive method, use sparingly\n        // returns both real and temporary labels\n        //\n        // the result is unordered (despite the two sources being sorted dicts)\n        public IEnumerable<KeyValuePair<int, IAnnotationLabel>> Labels => \n            cachedLabels ?? ConcatNormalAndTempLabels();\n\n        private IEnumerable<KeyValuePair<int, IAnnotationLabel>> ConcatNormalAndTempLabels() => \n            NormalProvider.Labels.Concat(TemporaryProvider.Labels);\n\n        public override IAnnotationLabel GetLabel(int snesAddress)\n        {\n            // if there's a real label (like, added in the Diz GUI), prefer that.\n            // if there's not, use an auto-generated label if it exists\n            var normalExisting = NormalProvider.GetLabel(snesAddress);\n            return normalExisting ?? TemporaryProvider.GetLabel(snesAddress);\n        }\n\n        public IMirroredLabelCacheSearch MirroredLabelCacheSearch => mirroredLabelCacheSearch;\n\n        public void DeleteAllLabels()\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            NormalProvider.DeleteAllLabels();\n            TemporaryProvider.DeleteAllLabels();\n            \n            OnLabelChanged?.Invoke(this, EventArgs.Empty);\n        }\n\n        public void RemoveLabel(int snesAddress)\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            // we should only operate on real labels here. ignore temporary labels\n            \n            NormalProvider.RemoveLabel(snesAddress);\n            \n            OnLabelChanged?.Invoke(this, EventArgs.Empty);\n        }\n\n        public void AddLabel(int snesAddress, IAnnotationLabel labelToAdd, bool overwrite = false)\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            // we should only operate on real labels here. ignore temporary labels.\n            // explicitly use AddTemporaryLabel() for temp stuff.\n            \n            NormalProvider.AddLabel(snesAddress, labelToAdd, overwrite);\n            \n            OnLabelChanged?.Invoke(this, EventArgs.Empty);\n        }\n\n        public void SetAll(Dictionary<int, IAnnotationLabel> newLabels)\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            ClearTemporaryLabels();\n            NormalProvider.SetAll(newLabels);\n            \n            OnLabelChanged?.Invoke(this, EventArgs.Empty);\n        }\n\n        public void AppendLabels(Dictionary<int, IAnnotationLabel> newLabels, bool smartMerge = false)\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n            \n            NormalProvider.AppendLabels(newLabels, smartMerge);\n            \n            OnLabelChanged?.Invoke(this, EventArgs.Empty);\n        }\n        \n        #region \"Equality\"\n        public bool Equals(LabelsServiceWithTemp other)\n        {\n            if (ReferenceEquals(null, other)) return false;\n            return ReferenceEquals(this, other) || Labels.SequenceEqual(other.Labels); // expensive, allocates memory for copy. probably ok though.\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            return obj.GetType() == GetType() && Equals((LabelsServiceWithTemp)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            unchecked\n            {\n                var hashCode = NormalProvider != null ? NormalProvider.GetHashCode() : 0;\n                hashCode = (hashCode * 397) ^ (TemporaryProvider != null ? TemporaryProvider.GetHashCode() : 0);\n                return hashCode;\n            }\n        }\n\n        public static bool operator ==(LabelsServiceWithTemp left, LabelsServiceWithTemp right)\n        {\n            return Equals(left, right);\n        }\n\n        public static bool operator !=(LabelsServiceWithTemp left, LabelsServiceWithTemp right)\n        {\n            return !Equals(left, right);\n        }\n        #endregion\n\n        public void SortLabels()\n        {\n            if (cachedLabels != null)\n                throw new InvalidOperationException(\"Cannot modify labels while cache is locked\");\n\n            NormalProvider.SortLabels();\n        }\n    }\n\n    #if DIZ_3_BRANCH\n    public class ByteSourceLabelProvider : LabelProviderBase, ILabelService, IEquatable<ByteSourceLabelProvider>\n    {\n        public bool Equals(ByteSourceLabelProvider other)\n        {\n            if (ReferenceEquals(null, other)) return false;\n            if (ReferenceEquals(this, other)) return true;\n            return ReferenceEquals(ByteSource, other.ByteSource);\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != this.GetType()) return false;\n            return Equals((ByteSourceLabelProvider)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            return ByteSource.GetHashCode();\n        }\n\n        public static bool operator ==(ByteSourceLabelProvider left, ByteSourceLabelProvider right)\n        {\n            return Equals(left, right);\n        }\n\n        public static bool operator !=(ByteSourceLabelProvider left, ByteSourceLabelProvider right)\n        {\n            return !Equals(left, right);\n        }\n\n        private ByteSource ByteSource { get; }\n        \n        // pass in topleve (i.e. Data.SnesAddressSpace)\n        public ByteSourceLabelProvider(ByteSource byteSource)\n        {\n            ByteSource = byteSource;\n        }\n\n        public IEnumerable<KeyValuePair<int, IAnnotationLabel>> Labels => \n            ByteSource.GetAnnotationsIncludingChildrenEnumerator<IAnnotationLabel>();\n\n        public static bool IsLabel(Annotation annotation) => annotation.GetType() == typeof(Label);\n        \n        public void DeleteAllLabels()\n        {\n            ByteSource.RemoveAllAnnotations(IsLabel);\n        }\n\n        public void RemoveLabel(int snesAddress)\n        {\n            ByteSource.RemoveAllAnnotationsAt(snesAddress, IsLabel);\n        }\n\n        public void SetAll(Dictionary<int, IAnnotationLabel> newLabels)\n        {\n            DeleteAllLabels();\n            foreach (var (key, value) in newLabels)\n            {\n                AddLabel(key, value);\n            }\n        }\n\n        public override Label GetLabel(int snesAddress) => ByteSource.GetOneAnnotation<Label>(snesAddress);\n\n        public void AddLabel(int snesAddress, IAnnotationLabel labelToAdd, bool overwrite = false)\n        {\n            Debug.Assert(labelToAdd != null);\n            \n            if (overwrite)\n                RemoveLabel(snesAddress);\n\n            var existing = ByteSource.GetOneAnnotation<IAnnotationLabel>(snesAddress);\n            \n            if (existing == null)\n                ByteSource.AddAnnotation(snesAddress, labelToAdd);\n        }\n    } \n    #endif\n    \n    public class LabelsCollection : LabelProviderBase, ILabelService, IEquatable<LabelsCollection>\n    {\n        // ReSharper disable once MemberCanBePrivate.Global\n        public Dictionary<int, IAnnotationLabel> Labels { get; private set; } = new();\n        \n        [XmlIgnore]\n        IEnumerable<KeyValuePair<int, IAnnotationLabel>> IReadOnlyLabelProvider.Labels => Labels;\n\n        public void AddLabel(int snesAddress, IAnnotationLabel labelToAdd, bool overwrite = false)\n        {\n            Debug.Assert(labelToAdd != null);\n            \n            if (overwrite)\n                RemoveLabel(snesAddress);\n\n            var existing = Labels.ContainsKey(snesAddress);\n\n            if (!existing)\n                Labels.Add(snesAddress, labelToAdd);\n        }\n\n        public void DeleteAllLabels()\n        {\n            Labels.Clear();\n        }\n        \n        public void RemoveLabel(int snesAddress)\n        {\n            Labels.Remove(snesAddress);\n        }\n\n        public void SetAll(Dictionary<int, IAnnotationLabel> newLabels)\n        {\n            DeleteAllLabels();\n            AppendLabels(newLabels);\n        }\n\n        public void AppendLabels(Dictionary<int, IAnnotationLabel> newLabels, bool smartMerge=false)\n        {\n            foreach (var snesAddress in newLabels.Keys)\n            {\n                if (!smartMerge) {\n                    Labels.Add(snesAddress, newLabels[snesAddress]);\n                    continue;\n                }\n                \n                // smart merging: try something a little better to preserve/merge existing data\n                if (!Labels.TryGetValue(snesAddress, out var label))\n                {\n                    // doesn't exist so just add normally\n                    Labels.Add(snesAddress, newLabels[snesAddress]);\n                }\n                else\n                {\n                    // does exist, so let's more smartly merge the label content\n                    // prefer incoming data, if it exists, to overwrite existing data\n                    var newLabelName = newLabels[snesAddress].Name;\n                    var newLabelComment = newLabels[snesAddress].Comment;\n                    \n                    label.Name = newLabelName == \"\" ? label.Name : newLabelName;\n                    label.Comment = newLabelComment == \"\" ? label.Comment : newLabelComment;\n                }\n            }\n        }\n        \n        public void SortLabels()\n        {\n            Labels = Labels\n                .OrderBy(x => x.Key)\n                .ToDictionary(x => x.Key, x => x.Value);\n        }\n\n        public override IAnnotationLabel GetLabel(int snesAddress) => \n            Labels.GetValueOrDefault(snesAddress);\n\n        // non-temp label provider will never use this\n        public IMirroredLabelCacheSearch MirroredLabelCacheSearch => null;\n\n        #region \"Equality\"\n        public bool Equals(LabelsCollection other)\n        {\n            if (ReferenceEquals(null, other)) return false;\n            if (ReferenceEquals(this, other)) return true;\n            return Equals(Labels, other.Labels);\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != this.GetType()) return false;\n            return Equals((LabelsCollection)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            return Labels != null ? Labels.GetHashCode() : 0;\n        }\n\n        public static bool operator ==(LabelsCollection left, LabelsCollection right)\n        {\n            return Equals(left, right);\n        }\n\n        public static bool operator !=(LabelsCollection left, LabelsCollection right)\n        {\n            return !Equals(left, right);\n        }\n        #endregion\n    }\n}"
  },
  {
    "path": "Diz.Core/model/Project.cs",
    "content": "﻿#nullable enable\n\nusing System.ComponentModel;\nusing System.ComponentModel.DataAnnotations;\nusing System.IO;\nusing System.Xml.Serialization;\nusing Diz.Core.export;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model.snes;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.util;\n\nnamespace Diz.Core.model;\n\npublic interface IProject : \n    INotifyPropertyChanged, \n    IProjectWithSession,\n    ISnesCachedVerificationInfo // see if we can get rid of this eventually. only needed for now for serialization\n{\n    public Data Data { get; }\n}\n\n[TypeConverter(typeof(ExpandableObjectConverter))]\npublic class ProjectSettings\n{\n    // any public properties here will be shown in the Tools -> Preferences menu\n    \n    [Category(\"Project save format settings\")]\n    [DisplayName(\"Project save format options\")]\n    [Description(\"Advanced options for tweaking how your .diz or .dizraw file will be saved (most people never need to mess with this). Takes effect when you save the project.\")]\n    public RomBytesOutputFormatSettings RomBytesOutputFormatSettings { get; set; } = new();\n\n    [Category(\"BSNES Import Options\")]\n    [DisplayName(\"Usage map / tracelog import only changes unmarked Rom Bytes\")]\n    [Description(\n        \"If true, usage map and tracelog imports/capture won't change anything you already marked. If False, your data will be overwritten from BSNES's usage map. \" +\n        \"(useful if you manually marked a lot of instructions incorrectly and they're desync'd. BSNES's marking is really good but not foolproof, \" +\n        \"and it has been known to get M/X flags incorrect rarely). Safest option is to leave this OFF\")]\n    public bool BsnesUsageMapImportOnlyChangedUnmarked { get; set; } = true;\n\n    public override string ToString() => \"\";\n}\n\npublic class ProjectUserSettings\n{\n    // these settings are saved per-project BUT are intended to be user-specific and not shared with all users\n    // i.e. unlike the main project file, the user won't check this into git\n    // (NOTE: there's a different settings file for global Application-specific stuff, and for stuff saved WITH the project intended to be shared) \n        \n    // current view offset (where are you scrolled in the UI)\n    public int CurrentViewOffset { get; set; }\n    \n    // attached ROM filename.\n    // the main project file will store the checksums/etc that this must match, or it'll ask for another rom file.\n    // this is important to keep locally only because we don't want any stored path or ROM filenames to leak into\n    // public git repos, potentially exposing people's sensitive user info/etc.\n    public string AttachedRomFilename { get; set; } = \"\";\n}\n\npublic class Project : IProject\n{\n    // Any public properties will be automatically serialized to XML unless noted.\n    // They will require a get AND set.\n    // Order is important.\n\n    [XmlIgnore]\n    public string ProjectFileName\n    {\n        get => projectFileName;\n        set => this.SetField(PropertyChanged, ref projectFileName, value);\n    }\n    \n    [XmlIgnore]\n    public string AttachedRomFilename\n    {\n        get => ProjectUserSettings.AttachedRomFilename ?? \"\";\n        set \n        {\n            if (ProjectUserSettings.AttachedRomFilename != value)\n            {\n                if (Session != null) Session.UnsavedChanges = true;\n            }\n\n            // below is same as: this.SetField(PropertyChanged, ProjectUserSettings.AttachedFromFilename, value);\n            \n            if (NotifyPropertyChangedExtensions.FieldIsEqual(ProjectUserSettings.AttachedRomFilename, value)) \n                return;\n            \n            ProjectUserSettings.AttachedRomFilename = value;\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AttachedRomFilename)));\n        }\n    }\n\n    // safety checks:\n    // The rom \"Game name\" and \"Checksum\" are copies of certain bytes from the ROM which\n    // get stored with the project file.  REMEMBER: We don't store the actual ROM bytes\n    // in the project file, so when we load a project, we must also open the same ROM and load its\n    // bytes in the project.\n    //\n    // Project = Metadata\n    // Rom = The real data\n    //\n    // If we load a ROM, and then its checksum and name don't match what we have stored in the XML,\n    // then we have an issue (i.e. not the same ROM, or it was modified, or missing, etc).\n    // The user must either provide a ROM matching these criteria, or abort loading the project.\n    public string InternalRomGameName\n    {\n        get => internalRomGameName;\n        set => this.SetField(PropertyChanged, ref internalRomGameName, value);\n    }\n\n    public uint InternalCheckSum\n    {\n        get => internalCheckSum;\n        set => this.SetField(PropertyChanged, ref internalCheckSum, value);\n    }\n\n    public LogWriterSettings LogWriterSettings\n    {\n        get => logWriterSettings with\n        {\n            BaseOutputPath = Session?.ProjectDirectory ?? \"\",\n        };\n        set => this.SetField(PropertyChanged, ref logWriterSettings, value);\n    }\n\n    // settings saved WITH the project file.\n    // for things that are expected to get checked into git and shared with all project users\n    public ProjectSettings ProjectSettings\n    {\n        get => projectSettings;\n        set => this.SetField(PropertyChanged, ref projectSettings, value);\n    }\n\n    // settings saved WITHOUT the project file.\n    // for things that are not expected to get checked into git and shared with all project users\n    [XmlIgnore]\n    public ProjectUserSettings ProjectUserSettings\n    {\n        get => projectUserSettings;\n        set => this.SetField(PropertyChanged, ref projectUserSettings, value);\n    }\n\n    // needs to come last for serialization. this is the heart of the app, the actual\n    // data from the ROM and metadata we add/create.\n    public Data Data\n    {\n        get => data!;\n        set => this.SetField(PropertyChanged, ref data, value);\n    }\n        \n    /// <summary>\n    /// Temporary session-specific data associated with this Project\n    /// </summary>\n    /// <remarks>\n    /// Never saved with XML, this is all temporary data that exists as long as its open\n    /// in the app, and no longer.\n    /// </remarks>\n    [XmlIgnore]\n    public IProjectSession? Session\n    {\n        get => session;\n        set\n        {\n            var previouslyUnsaved = false;\n\n            if (session != null)\n            {\n                session.PropertyChanged -= SessionOnPropertyChanged;\n                previouslyUnsaved = session.UnsavedChanges;\n            }\n\n            this.SetField(PropertyChanged, ref session, value);\n\n            if (session == null) \n                return;\n                \n            session.PropertyChanged += SessionOnPropertyChanged;\n            session.UnsavedChanges = previouslyUnsaved;\n        }\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    public Project()\n    {\n        logWriterSettings = new LogWriterSettings();\n        PropertyChanged += ProjectPropertyChanged;\n    }\n        \n    private string GetAbsolutePathToRomFile()\n    {\n        var pathToProjectFile = GetFullBasePathToRomFile(session?.ProjectFileName ?? \"\");\n        var attachedRomFileNoPath = Path.GetFileName(AttachedRomFilename);\n        return Path.Combine(pathToProjectFile, attachedRomFileNoPath);\n    }\n        \n    private string? GetFullBasePathToRomFile(string projFileName)\n    {\n        var projDir = session?.ProjectDirectory ?? \"\";\n        if (projDir != \"\")\n            return projDir;\n                \n        return projFileName != \"\" ? Util.GetDirNameOrEmpty(projFileName) : \"\";\n    }\n        \n    private void SessionOnPropertyChanged(object? sender, PropertyChangedEventArgs e)\n    {\n        switch (e.PropertyName)\n        {\n            case nameof(IProjectSession.ProjectFileName):\n                AttachedRomFilename = GetAbsolutePathToRomFile();\n                break;\n        }\n            \n        PropertyChanged?.Invoke(sender, e);\n    }\n\n    private void ProjectPropertyChanged(object? sender, PropertyChangedEventArgs e)\n    {\n        if (e.PropertyName != nameof(IProjectSession.UnsavedChanges) && Session != null)\n            Session.UnsavedChanges = true;\n\n        // try not to get too fancy in this method.\n    }\n\n    // don't access these backing fields directly, instead, always use the properties\n    private string projectFileName = \"\";\n    private string internalRomGameName = \"\";\n    private uint internalCheckSum;\n    private Data? data; // TODO: change to IData\n    private LogWriterSettings logWriterSettings;\n    private IProjectSession? session;\n    private ProjectSettings projectSettings = new();\n    private ProjectUserSettings projectUserSettings = new();\n\n    #region Equality\n    protected bool Equals(Project other)\n    {\n        return ProjectFileName == other.ProjectFileName && AttachedRomFilename == other.AttachedRomFilename && Equals(Data, other.Data) && InternalRomGameName == other.InternalRomGameName && InternalCheckSum == other.InternalCheckSum;\n    }\n\n    public override bool Equals(object? obj)\n    {\n        if (ReferenceEquals(null, obj)) return false;\n        if (ReferenceEquals(this, obj)) return true;\n        if (obj.GetType() != this.GetType()) return false;\n        return Equals((Project) obj);\n    }\n\n    public override int GetHashCode()\n    {\n        unchecked\n        {\n            var hashCode = (ProjectFileName != null ? ProjectFileName.GetHashCode() : 0);\n            hashCode = (hashCode * 397) ^ (AttachedRomFilename != null ? AttachedRomFilename.GetHashCode() : 0);\n            hashCode = (hashCode * 397) ^ (Data != null ? Data.GetHashCode() : 0);\n            hashCode = (hashCode * 397) ^ (InternalRomGameName != null ? InternalRomGameName.GetHashCode() : 0);\n            hashCode = (int) ((hashCode * 397) ^ InternalCheckSum);\n            return hashCode;\n        }\n    }\n    #endregion\n}\n    \npublic interface IProjectWithSession {\n    IProjectSession? Session { get; set; }\n    string AttachedRomFilename { get; }\n}\n    \npublic interface IProjectSession : INotifyPropertyChanged\n{\n    public string? ProjectDirectory { get; }\n    string AttachedRomFileFullPath { get; }\n    string ProjectFileName { get; set; }\n    bool UnsavedChanges { get; set; }\n}\n    \n    \n/// <summary>\n/// temporary data stored about the current project \"session\"\n/// i.e. mostly stuff we don't want serialized to XML that may change\n/// from run to run of the app (like working dir,etc)\n/// stuff in here might want to be saved somewhere else. \n/// </summary>\npublic class ProjectSession : IProjectSession\n{\n    // cache of the last filename this project was saved as.\n    // (This field may require some rework for GUI multi-project support)\n    public string ProjectFileName\n    {\n        get => projectFileName;\n        set => this.SetField(PropertyChanged, ref projectFileName, value);\n    }\n        \n    public bool UnsavedChanges\n    {\n        get => unsavedChanges;\n        set => this.SetField(PropertyChanged, ref unsavedChanges, value);\n    }\n        \n    public string? ProjectDirectory =>\n        Util.GetDirNameOrEmpty(projectFileName);\n        \n    public string AttachedRomFileFullPath =>\n        Path.Combine(ProjectDirectory, project.AttachedRomFilename);\n\n    private readonly IProjectWithSession project;\n        \n    private string projectFileName;\n    private bool unsavedChanges;\n\n    public ProjectSession(IProjectWithSession project, string projectFileName)\n    {\n        this.projectFileName = projectFileName;\n        this.project = project;\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n}"
  },
  {
    "path": "Diz.Core/model/ProjectFactory.cs",
    "content": "﻿using System;\nusing Diz.Core.serialization;\n\nnamespace Diz.Core.model;\n\npublic interface IProjectFactory\n{\n    public IProject Create();\n}\n\npublic class ProjectFactory : IProjectFactory\n{\n    private readonly IDataFactory dataFactory;\n\n    public ProjectFactory(IDataFactory dataFactory)\n    {\n        this.dataFactory = dataFactory;\n    }\n\n    public IProject Create() =>\n        new Project\n        {\n            Data = dataFactory.Create()\n        };\n}\n\npublic interface IProjectImporter\n{\n    /// <summary>\n    /// Plug and pray, try and create a new project by importing Rom with the default auto-detected settings,\n    /// no opportunity to interactively change them.\n    /// Use this function more for things like automated tests etc, try not to use it for normal functionality\n    /// in the app (instead, use other classes that go through the process or controllers+gui)\n    /// </summary>\n    /// <param name=\"romFilename\">Name of a SNES ROM that will be used for this project</param>\n    /// <returns>Newly created project</returns>\n    IProject CreateProjectFromDefaultSettings(string romFilename);\n    \n    /// <summary>\n    /// Import a ROM file using the given settings.\n    /// This is the preferred way to create projects from disk.\n    /// Typically you will want to use a GUI or other interactive methods to populate the settings first, then\n    /// call this to do the actual \"import\" which creates the final project and data ready for use. \n    /// </summary>\n    /// <param name=\"importSettings\">Name of a SNES ROM that will be used for this project</param>\n    /// <returns>Newly created project</returns>\n    IProject CreateProjectFromSettings(IRomImportSettings importSettings);\n}\n\npublic interface IProjectImportDefaultSettingsFactory : IProjectImportSettingsFactory\n{\n    // we can replace this interface with a named service \n}\n\npublic class ProjectImporter : IProjectImporter\n{\n    private readonly IProjectImportDefaultSettingsFactory generateDefaultSettingsFromRom;\n    private readonly Func<IRomImportSettings, IProjectFactoryFromRomImportSettings> createImporterFromSettings;\n\n    public ProjectImporter(\n        IProjectImportDefaultSettingsFactory generateDefaultSettingsFromRom, \n        Func<IRomImportSettings, IProjectFactoryFromRomImportSettings> createImporterFromSettings)\n    {\n        this.generateDefaultSettingsFromRom = generateDefaultSettingsFromRom;\n        this.createImporterFromSettings = createImporterFromSettings;\n    }\n    \n    public IProject CreateProjectFromDefaultSettings(string romFilename)\n    {\n        var settings = generateDefaultSettingsFromRom.Create(romFilename);\n        return CreateProjectFromSettings(settings);\n    }\n\n    public IProject CreateProjectFromSettings(IRomImportSettings importSettings)\n    {\n        return createImporterFromSettings(importSettings)?.Read();\n    }\n}\n\n"
  },
  {
    "path": "Diz.Core/model/ProjectImportSettingsFactory.cs",
    "content": "﻿using Diz.Core.serialization;\n\nnamespace Diz.Core.model;\n\npublic interface IProjectImportSettingsFactory\n{\n    IRomImportSettings Create(string romFilename = null);\n}\n\n// ReSharper disable once ClassNeverInstantiated.Global\npublic class ProjectImportSettingsFactory : IProjectImportSettingsFactory\n{\n    public IRomImportSettings Create(string romFilename = null) =>\n        new ImportRomSettings\n        {\n            RomFilename = romFilename\n        };\n}"
  },
  {
    "path": "Diz.Core/model/ROMByte.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Threading;\nusing Diz.Core.Interfaces;\nusing Diz.Core.util;\n\n#nullable enable\n\nnamespace Diz.Core.model\n{\n    // represents metadata associated with each byte of the ROM\n    // RomByteData is just the data itself with as little associated fluff as possible \n    public class RomByteData : INotifyPropertyChangedExt\n    {\n        // never modify directly. only go through the public fields\n        private byte rom;\n        private byte dataBank;\n        private int directPage;\n        private bool xFlag;\n        private bool mFlag;\n        private FlagType typeFlag = FlagType.Unreached;\n        private Architecture arch = Architecture.Cpu65C816;\n        private InOutPoint point = 0;\n\n        // holds the original byte from the source ROM\n        public byte Rom\n        {\n            get => rom;\n            set => this.SetField(PropertyChanged, ref rom, value);\n        } // never serialize this, read from ROM on load. (for copyright reasons)\n\n        // everything else is metadata that describes the source byte above\n        public byte DataBank\n        {\n            get => dataBank;\n            set => this.SetField(PropertyChanged, ref dataBank, value);\n        }\n\n        public int DirectPage\n        {\n            get => directPage;\n            set => this.SetField(PropertyChanged, ref directPage, value);\n        }\n\n        public bool XFlag\n        {\n            get => xFlag;\n            set => this.SetField(PropertyChanged, ref xFlag, value);\n        }\n\n        public bool MFlag\n        {\n            get => mFlag;\n            set => this.SetField(PropertyChanged, ref mFlag, value);\n        }\n\n        public FlagType TypeFlag\n        {\n            get => typeFlag;\n            set => this.SetField(PropertyChanged, ref typeFlag, value);\n        }\n\n        public Architecture Arch\n        {\n            get => arch;\n            set => this.SetField(PropertyChanged, ref arch, value);\n        }\n\n        public InOutPoint Point\n        {\n            get => point;\n            set => this.SetField(PropertyChanged, ref point, value);\n        }\n\n        // don't serialize. cached copy of our offset in parent collection\n        public int Offset { get; private set; } = -1;\n\n        public void SetCachedOffset(int offset)\n        {\n            // not in love with this or that we're caching it. would be cool if we didn't\n            // need Offset, or could just derive this (quickly) from the base list.\n            Offset = offset;\n        }\n\n\n        #region Equality\n\n        private bool Equals(RomByte other)\n        {\n            return Rom == other.Rom && EqualsButNoRomByte(other);\n        }\n\n        public bool EqualsButNoRomByte(RomByte other)\n        {\n            return DataBank == other.DataBank && DirectPage == other.DirectPage && XFlag == other.XFlag && MFlag == other.MFlag && TypeFlag == other.TypeFlag && Arch == other.Arch && Point == other.Point;\n        }\n\n        public override bool Equals(object? obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            return obj.GetType() == this.GetType() && Equals((RomByte)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            unchecked\n            {\n                var hashCode = Rom.GetHashCode();\n                hashCode = (hashCode * 397) ^ DataBank.GetHashCode();\n                hashCode = (hashCode * 397) ^ DirectPage;\n                hashCode = (hashCode * 397) ^ XFlag.GetHashCode();\n                hashCode = (hashCode * 397) ^ MFlag.GetHashCode();\n                hashCode = (hashCode * 397) ^ (int)TypeFlag;\n                hashCode = (hashCode * 397) ^ (int)Arch;\n                hashCode = (hashCode * 397) ^ (int)Point;\n                return hashCode;\n            }\n        }\n        #endregion\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        public void OnPropertyChanged(string propertyName)\n        {\n        }\n    }\n\n    // wrap RomByteData with extra helper stuff like locking\n    public class RomByte : RomByteData, IRomByte\n    {\n        // note: our thread safety isn't comprehensive in this project yet.\n        // be careful with this if you're doing anything clever, especially writing.\n        // also, we should kill this and replace the container with a thread-safe one or something.\n        public ReaderWriterLockSlim Lock { get; } = new();\n        bool IRomByte.EqualsButNoRomByte(IRomByte other)\n        {\n            return other is RomByte romByte && EqualsButNoRomByte(romByte);\n        }\n    }\n}\n"
  },
  {
    "path": "Diz.Core/model/RomBytes.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Collections.Specialized;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\n\nnamespace Diz.Core.model\n{\n    public class RomBytes : IRomBytes<RomByte>\n    {\n        private ObservableCollection<RomByte> bytes;\n\n        // TODO: might be able to do something more generic for RomBytes now that other refactorings are completed.\n        // This class needs to do these things that are special:\n        // 1) Be handled specially by our custom XML serializer (compresses to save disk space)\n        // 2) Handle Equals() by comparing each element in the list (SequenceEqual)\n        // 3) Emit notifypropertychanged if any members change\n        private ObservableCollection<RomByte> Bytes\n        {\n            get => bytes;\n            set\n            {\n                bytes = value;\n\n                bytes.CollectionChanged += Bytes_CollectionChanged;\n                foreach (var romByte in bytes)\n                {\n                    romByte.PropertyChanged += RomByteObjectChanged;\n                }\n            }\n        }\n\n        public RomByte this[int i]\n        {\n            get => Bytes[i];\n            set => Bytes[i] = value;\n        }\n\n        IRomByte IRomBytes<RomByte>.this[int i] => Bytes[i];\n\n        public RomBytes()\n        {\n            Bytes = new ObservableCollection<RomByte>();\n        }\n\n        public void SetFrom(RomByte[] romBytes)\n        {\n            Bytes = new ObservableCollection<RomByte>(romBytes);\n            for (var i = 0; i < romBytes.Length; ++i)\n            {\n                romBytes[i].SetCachedOffset(i);\n            }\n        }\n        \n        public void SetBytesFrom(IReadOnlyList<byte> copyFrom, int dstStartingOffset)\n        {\n            for (var i = 0; i < copyFrom.Count; ++i) \n                this[i + dstStartingOffset].Rom = copyFrom[i];\n        }\n\n        public int Count => Bytes.Count;\n        public bool SendNotificationChangedEvents { get; set; } = true;\n\n        public void Add(RomByte romByte)\n        {\n            Bytes.Add(romByte);\n            romByte.SetCachedOffset(Bytes.Count - 1); // I don't love this....\n        }\n\n        public void Create(int size)\n        {\n            for (var i = 0; i < size; ++i)\n                Add(new RomByte());\n        }\n        public void Clear()\n        {\n            Bytes.Clear();\n        }\n\n        #region Equality\n        protected bool Equals(RomBytes other)\n        {\n            return Bytes.SequenceEqual(other.Bytes);\n        }\n\n        public override bool Equals(object obj)\n        {\n            if (ReferenceEquals(null, obj)) return false;\n            if (ReferenceEquals(this, obj)) return true;\n            if (obj.GetType() != this.GetType()) return false;\n            return Equals((RomBytes)obj);\n        }\n\n        public override int GetHashCode()\n        {\n            return (Bytes != null ? Bytes.GetHashCode() : 0);\n        }\n        #endregion\n\n        #region Enumerator\n        public IEnumerator<RomByte> GetEnumerator()\n        {\n            return Bytes.GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n        #endregion\n\n        public event NotifyCollectionChangedEventHandler CollectionChanged;\n        public event PropertyChangedEventHandler PropertyChanged;\n\n        private void Bytes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)\n        {\n            if (e.NewItems != null)\n                foreach (RomByte item in e.NewItems)\n                    item.PropertyChanged += RomByteObjectChanged;\n\n            if (e.OldItems != null)\n                foreach (RomByte item in e.OldItems)\n                    item.PropertyChanged -= RomByteObjectChanged;\n\n            if (SendNotificationChangedEvents)\n                CollectionChanged?.Invoke(sender, e);\n        }\n\n        private void RomByteObjectChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)\n        {\n            if (SendNotificationChangedEvents)\n                PropertyChanged?.Invoke(sender, e);\n        }\n    }\n}\n\n\npublic static class RomBytesExtensions\n{\n    public static void CreateRomBytesFromRom(this RomBytes @this, IEnumerable<byte> actualRomBytes)\n    {\n        Debug.Assert(@this.Count == 0);\n            \n        var previousNotificationState = @this.SendNotificationChangedEvents;\n        @this.SendNotificationChangedEvents = false;\n\n        @this.Clear();\n        foreach (var fileByte in actualRomBytes)\n        {\n            @this.Add(new RomByte\n            {\n                Rom = fileByte,\n            });\n        }\n\n        @this.SendNotificationChangedEvents = previousNotificationState;\n    }\n        \n    public static byte[] GetRomBytes(this IData @this, int pcOffset, int count)\n    {\n        var output = new byte[count];\n        for (var i = 0; i < output.Length; i++)\n            output[i] = (byte)@this.GetRomByte(pcOffset + i);\n\n        return output;\n    }\n\n    public static void CopyRomDataIn(this IRomBytes<IRomByte> @this, IEnumerable<byte> trueRomBytes)\n    {\n        var previousNotificationState = @this.SendNotificationChangedEvents;\n        @this.SendNotificationChangedEvents = false;\n            \n        var i = 0;\n        foreach (var b in trueRomBytes)\n        {\n            @this[i].Rom = b;\n            ++i;\n        }\n        Debug.Assert(@this.Count == i);\n\n        @this.SendNotificationChangedEvents = previousNotificationState;\n    }\n}\n"
  },
  {
    "path": "Diz.Core/model/project/DataUtils.cs",
    "content": "﻿using Diz.Core.model.snes;\n\nnamespace Diz.Core.model.project;\n\npublic class DataFactory : IDataFactory\n{\n    // TODO: make this be IData not just Data. it will require a lot more rework though\n    // to update all client code to only use IData and ISnesApi<IData>, so, for now, skipping that.\n    //\n    // Note: decorators may override this and add different APIs/etc\n    public Data Create() => new();\n}"
  },
  {
    "path": "Diz.Core/model/project/LinkedRomBytesFileSearchProvider.cs",
    "content": "﻿#nullable enable\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.model.project;\n\npublic interface IReadFromFileBytes\n{\n    byte[] ReadRomFileBytes(string filename);\n}\n\npublic class ReadFromFileBytes : IReadFromFileBytes\n{\n    public byte[] ReadRomFileBytes(string filename) => \n        RomUtil.ReadRomFileBytes(filename);\n}\n\npublic delegate string? FilenameGetter(string reasonLastFailed);\npublic delegate void CompatibilityEnforcer(string filename, byte[] fileContents);\n    \npublic interface ILinkedRomBytesProvider\n{\n    // caller should throw InvalidDataException if it's not compatible\n    public CompatibilityEnforcer? EnsureCompatible { get; set; }\n        \n    // caller will be prompted to provide another filename to try, or return null if search should end\n    public FilenameGetter? GetNextFilename { get; set; }\n        \n    (string filename, byte[] romBytes)? SearchAndReadFromCompatibleRom(string initialRomFile, string otherPromptText = \"\");\n}\n    \n[UsedImplicitly]\npublic class LinkedRomBytesFileSearchProvider : ILinkedRomBytesProvider\n{\n    private readonly IReadFromFileBytes romBytesReader;\n        \n    public LinkedRomBytesFileSearchProvider(IReadFromFileBytes romBytesReader)\n    {\n        this.romBytesReader = romBytesReader;\n    }\n\n    public CompatibilityEnforcer? EnsureCompatible { get; set; }\n    public FilenameGetter? GetNextFilename { get; set; }\n\n    public (string filename, byte[] romBytes)? SearchAndReadFromCompatibleRom(string? initialRomFile, string otherPromptText = \"\")\n    {\n        var candidateFile = initialRomFile;\n            \n        while (candidateFile != null)\n        {\n            var bytes = GetRomFileContentsIfCompatible(candidateFile, out var lastFailureReason);\n            if (bytes != null)\n            {\n                Debug.Assert(string.IsNullOrEmpty(lastFailureReason));\n                return (candidateFile, bytes);\n            }\n\n            var promptText = $\"Project:'{otherPromptText.Trim()}'\\n{lastFailureReason}\";\n            candidateFile = GetNextFilename?.Invoke(promptText);\n        }\n\n        return null;\n    }\n\n    private byte[]? GetRomFileContentsIfCompatible(string romFilename, out string reasonFailed)\n    {\n        reasonFailed = \"\";\n\n        var bytes = GetRomFileBytesOrFailReason(romFilename, out reasonFailed);\n        if (bytes == null)\n            return null;\n\n        try\n        {\n            EnsureCompatible?.Invoke(romFilename, bytes);\n            return bytes;\n        }\n        catch (InvalidDataException ex)\n        {\n            reasonFailed = ex.Message;\n            return null;\n        }\n    }\n\n    private byte[]? GetRomFileBytesOrFailReason(string romFilename, out string reasonFailed)\n    {\n        reasonFailed = \"\";\n        \n        try\n        {\n            // call this even if romFilename is empty, because unit tests mock this reader and rely on it being empty\n            return romBytesReader.ReadRomFileBytes(romFilename);\n        }\n        catch (Exception ex)\n        {\n            reasonFailed = ex.Message;\n            \n            // provide a better error message in this case:\n            if (romFilename == \"\")\n            {\n                // you can hit this when upgrading from older project files to newer ones which don't store the \n                // ROM file directly (they store it in the prefs). this is OK and somewhat expected.\n                reasonFailed = \"ROM file not yet specified in project data.\\nThis is expected if opening a project for the first time, or, upgrading Diz.\\nPlease Link a new ROM file to continue.\";\n            }\n            \n            return null;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/model/project/ProjectRomBytesInit.cs",
    "content": "﻿#nullable enable\n\n// TODO: remove this file\nnamespace Diz.Core.model.project\n{\n\n\n}"
  },
  {
    "path": "Diz.Core/model/snes/Data.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Xml.Serialization;\nusing Diz.Core.Interfaces;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.model.snes;\n\npublic class Data : IData\n{\n    [XmlIgnore] public IDataStoreProvider<IArchitectureApi> Apis { get; } = new DataStoreProvider<IArchitectureApi>();\n    public IDataStoreProvider<IDataTag> Tags { get; } = new DataStoreProvider<IDataTag>();\n\n    private SortedDictionary<int, string> comments;\n    private ObservableCollection<IRegion> regions = [];\n    private RomBytes romBytes;\n\n    // NOTE: snes specific stuff (rom map mode/speed) should eventually be removed from here.\n    // this class should be a generic base class for all systems (snes, nes, sega, whatever).\n    // for now we're in transition.\n    // .. also, same thing with log generation stuff.\n\n    // don't modify these directly, always go through the public properties so\n    // other objects can subscribe to modification notifications\n    private RomMapMode romMapMode;\n    private RomSpeed romSpeed = RomSpeed.Unknown;\n\n    // Note: order of these public properties matters for the load/save process. Keep 'RomBytes' LAST\n    // TODO: should be a way in the XML serializer to control the order, remove this comment\n    // when we figure it out.\n    public RomMapMode RomMapMode\n    {\n        get => romMapMode;\n        set => this.SetField(PropertyChanged, ref romMapMode, value);\n    }\n\n    public RomSpeed RomSpeed\n    {\n        get => romSpeed;\n        set => this.SetField(PropertyChanged, ref romSpeed, value);\n    }\n\n    // next 2 dictionaries store in SNES address format (since memory labels can't be represented as a PC address)\n    public SortedDictionary<int, string> Comments\n    {\n        get => comments;\n        set => this.SetField(PropertyChanged, ref comments, value);\n    }\n\n    // for deserialization/loading in Diz2.0\n    // TODO: this is kind of a hack needs rework. would be better to ditch this and write some kind of custom\n    // deserializer that handles this instead\n    public Dictionary<int, IAnnotationLabel> LabelsSerialization\n    {\n        get => new(Labels.Labels);\n        set => Labels.SetAll(value);\n    }\n    \n    // RomBytes stored as PC file offset addresses (since ROM will always be mapped to disk)\n    public RomBytes RomBytes\n    {\n        get => romBytes;\n        set => this.SetField(PropertyChanged, ref romBytes, value);\n    }\n    IRomBytes<IRomByte> IRomBytesProvider.RomBytes => romBytes;\n    \n    public ObservableCollection<IRegion> Regions\n    {\n        get => regions;\n        set => this.SetField(PropertyChanged, ref regions, value);\n    }\n    [XmlIgnore] ObservableCollection<IRegion> IRegionProvider.Regions => Regions;\n\n\n    [XmlIgnore] public bool RomBytesLoaded { get; set; }\n\n    public Data()\n    {\n        comments = new SortedDictionary<int, string>();\n        Labels = new LabelsServiceWithTemp(this);\n        romBytes = new RomBytes();\n    }\n\n    public int GetRomSize() =>\n        RomBytes?.Count ?? 0;\n\n    public Architecture GetArchitecture(int i) => RomBytes[i].Arch;\n    public void SetArchitecture(int i, Architecture arch) => RomBytes[i].Arch = arch;\n\n    [CanBeNull]\n    public string GetComment(int snesAddress) =>\n        Comments.GetValueOrDefault(snesAddress);\n\n    public string GetCommentText(int snesAddress)\n    {\n        // option 1: use the comment text first\n        var comment = GetComment(snesAddress);\n        if (!string.IsNullOrEmpty(comment))\n            return comment;\n\n        // option 2: if a real comment doesn't exist, try see if our label itself has a comment attached, display that.\n        // TODO: this is convenient for display but might mess up setting. we probably should do this\n        // only in views, remove from here.\n        return Labels.GetLabelComment(snesAddress) ?? \"\";\n    }\n\n    public void AddComment(int i, string v, bool overwrite)\n    {\n        if (v == null)\n        {\n            Comments.Remove(i);\n        }\n        else\n        {\n            if (Comments.ContainsKey(i) && overwrite)\n                Comments.Remove(i);\n\n            Comments.TryAdd(i, v);\n        }\n    }\n\n    public byte? GetRomByte(int pcOffset)\n    {\n        return pcOffset >= RomBytes.Count ? null : RomBytes[pcOffset].Rom;\n    }\n\n    public byte? GetSnesByte(int snesAddress)\n    {\n        return GetRomByte(ConvertSnesToPc(snesAddress));\n    }\n\n    public int? GetRomWord(int offset)\n    {\n        if (offset + 1 >= GetRomSize())\n            return null;\n\n        var rb1Null = GetRomByte(offset);\n        var rb2Null = GetRomByte(offset + 1);\n        if (!rb1Null.HasValue || !rb2Null.HasValue)\n            return null;\n\n        return rb1Null + (rb2Null << 8);\n    }\n\n    public int? GetRomLong(int offset)\n    {\n        if (offset + 2 >= GetRomSize())\n            return null;\n\n        var romWord = GetRomWord(offset);\n        var rb3Null = GetRomByte(offset + 2);\n        if (!romWord.HasValue || !rb3Null.HasValue)\n            return null;\n\n        return romWord + (rb3Null << 16);\n    }\n\n    public int? GetRomDoubleWord(int offset)\n    {\n        if (offset + 3 >= GetRomSize())\n            return null;\n\n        var romLong = GetRomLong(offset);\n        var rb4Null = GetRomByte(offset + 3);\n        if (!romLong.HasValue || !rb4Null.HasValue)\n            return null;\n\n        return romLong + (rb4Null << 24);\n    }\n\n    public int ConvertPCtoSnes(int offset)\n    {\n        return RomUtil.ConvertPCtoSnes(offset, RomMapMode, RomSpeed);\n    }\n\n    public int ConvertSnesToPc(int address)\n    {\n        return RomUtil.ConvertSnesToPc(address, RomMapMode, GetRomSize());\n    }\n\n    public int Mark(Action<int> markAction, int offset, int count)\n    {\n        int i, size = GetRomSize();\n        for (i = 0; i < count && offset + i < size; i++)\n            markAction(offset + i);\n\n        return offset + i < size\n            ? offset + i\n            : size - 1;\n    }\n\n    // get the actual ROM file bytes (i.e. the contents of the SMC file on the disk)\n    // note: don't save these anywhere permanent because ROM data is usually copyrighted.\n    public IEnumerable<byte> GetFileBytes() =>\n        RomBytes.Select(b => b.Rom);\n\n    #region Equality\n\n    protected bool Equals(Data other)\n    {\n        return Labels.Equals(other.Labels) && RomMapMode == other.RomMapMode && RomSpeed == other.RomSpeed &&\n               Comments.SequenceEqual(other.Comments) && RomBytes.Equals(other.RomBytes);\n    }\n\n    public override bool Equals(object obj)\n    {\n        if (ReferenceEquals(null, obj)) return false;\n        if (ReferenceEquals(this, obj)) return true;\n        return obj.GetType() == this.GetType() && Equals((Data)obj);\n    }\n\n    public override int GetHashCode()\n    {\n        unchecked\n        {\n            var hashCode = Labels.GetHashCode();\n            hashCode = (hashCode * 397) ^ (int)RomMapMode;\n            hashCode = (hashCode * 397) ^ (int)RomSpeed;\n            hashCode = (hashCode * 397) ^ Comments.GetHashCode();\n            hashCode = (hashCode * 397) ^ RomBytes.GetHashCode();\n            return hashCode;\n        }\n    }\n\n    #endregion\n\n    [XmlIgnore] public LabelsServiceWithTemp Labels { get; }\n    [XmlIgnore] ILabelServiceWithTempLabels IData.Labels => Labels;\n\n    [CanBeNull] public IRegion CreateNewRegion() => new Region();\n    [CanBeNull]  public IRegion GetRegion(int snesAddress)\n    {\n        return Regions\n            .Where(region => snesAddress >= region.StartSnesAddress && snesAddress < region.EndSnesAddress)\n            .OrderByDescending(region => region.Priority)\n            .FirstOrDefault();\n    }\n    \n    public event PropertyChangedEventHandler PropertyChanged;\n}\n\npublic class Region : IRegion\n{\n    private int startSnesAddress;\n    private int endSnesAddress;\n    private string regionName;\n    private string contextToApply;\n    private int priority;\n    private bool exportSeparateFile;\n\n    public int StartSnesAddress\n    {\n        get => startSnesAddress;\n        set => this.SetField(PropertyChanged, ref startSnesAddress, value);\n    }\n\n    public int EndSnesAddress\n    {\n        get => endSnesAddress;\n        set => this.SetField(PropertyChanged, ref endSnesAddress, value);\n    }\n\n    public string RegionName\n    {\n        get => regionName;\n        set => this.SetField(PropertyChanged, ref regionName, value);\n    }\n\n    public string ContextToApply\n    {\n        get => contextToApply;\n        set => this.SetField(PropertyChanged, ref contextToApply, value);\n    }\n\n    public int Priority\n    {\n        get => priority;\n        set => this.SetField(PropertyChanged, ref priority, value);\n    }\n    \n    public bool ExportSeparateFile\n    {\n        get => exportSeparateFile;\n        set => this.SetField(PropertyChanged, ref exportSeparateFile, value);\n    }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n}"
  },
  {
    "path": "Diz.Core/model/snes/DataTags.cs",
    "content": "﻿using Diz.Core.Interfaces;\n\nnamespace Diz.Core.model.snes;\n\npublic class SampleDataGenerationTag : IDataTag\n{\n    /// <summary>\n    /// Store the original number of bytes generated by a sample ROM generator. \n    /// The sample data generator builds a full bank, but, it does so by padding up to the next bank size.\n    /// This field stores the original number of bytes generated, before padding took place.\n    ///\n    /// This is useful info for some subsystems like the assembly exporter, which generates labels that\n    /// might fall outside the range of the original sample data.\n    ///\n    /// If set to -1, no padding occured.\n    /// Only sample data created by Diz will have this field. It does not need to be serialized.\n    ///\n    /// Don't use this field for anything that's actually important.\n    /// </summary>\n    public int OriginalRomSizeBeforePadding { get; init; } = -1;\n}"
  },
  {
    "path": "Diz.Core/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.9.6, )\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[11.9.0, )\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2023.3.0, )\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.5.0, )\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"LightInject\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.6.4, )\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.4.2, )\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.3.0, )\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Core/serialization/FileByteProviderMultipleFiles.cs",
    "content": "﻿#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace Diz.Core.serialization;\n\npublic class FileByteProviderMultipleFiles : IFileByteProvider\n{\n    public byte[] ReadAllBytes(string filename)\n    {\n        if (!filename.EndsWith(\".dizdir\"))\n        {\n            // Not a special file, read all bytes normally and we're done!\n            return File.ReadAllBytes(filename);\n        }\n\n        return ReadMultipleSaveFiles(filename);\n    }\n\n    private static byte[] ReadMultipleSaveFiles(string filename)\n    {\n        // Special file found, parse contents.\n        var contents = File.ReadAllText(filename);\n        if (!contents.StartsWith(\"DIZ-MULTIFILE:\") || !contents.EndsWith(\"version=1\"))\n        {\n            throw new Exception(\"Invalid file format!\");\n        }\n\n        // Get the directory with the same name as the file (without extension)\n        var baseFilenameWithoutExtension = Path.GetFileNameWithoutExtension(filename);\n        var directoryName = Path.Combine(Path.GetDirectoryName(filename) ?? string.Empty, baseFilenameWithoutExtension);\n\n        // Regular expression to exactly match our file name pattern\n        var regex = new Regex(@\"^\\d{5}_save.*\\.txt$\");\n\n        // Get all files from the directory.\n        var allFiles = Directory.GetFiles(directoryName);\n    \n        // Throw an error if there are files not matching the pattern.\n        if (allFiles.Any(file => !regex.IsMatch(Path.GetFileName(file))))\n        {\n            throw new Exception(\"There are files that do not match the required pattern!\");\n        }\n\n        // Filter files to match our pattern and order them.\n        var matchedFiles = allFiles.Where(path => regex.IsMatch(Path.GetFileName(path)))\n            .OrderBy(f => f)\n            .ToList();\n\n        // Read all files in the correct order and concatenate their bytes.\n        // we ONLY ORDER BY THE NUMBER IN FRONT, and nothing else.\n        var result = new List<byte>();\n        foreach (var fileBytes in matchedFiles.Select(File.ReadAllBytes))\n        {\n            result.AddRange(fileBytes);\n        }\n\n        return result.ToArray();\n    }\n\n    public void WriteBytes(string filename, byte[] data)\n    {\n        // Make a tmp dir for output\n        var baseFilenameWithoutExtension = Path.GetFileNameWithoutExtension(filename);\n        var directoryName = Path.GetDirectoryName(filename) ?? string.Empty;\n        var tempDirPath = Path.Combine(directoryName, baseFilenameWithoutExtension + \".tmp\");\n        if (Directory.Exists(tempDirPath))\n            Directory.Delete(tempDirPath, true);\n        Directory.CreateDirectory(tempDirPath);\n\n        // read the bytes we're supposed to write (originally to just one file, but..)\n        var content = Encoding.UTF8.GetString(data);\n        if (content == null)\n            throw new InvalidOperationException(\"Content cannot be null.\");\n\n        // instead, split the output into a bunch different files in one directory \n        new FileSplitWriter(tempDirPath).OutputSplitFiles(content);\n\n        // Rename the .tmp dir to the final directory\n        var previousDir = Path.Combine(directoryName, baseFilenameWithoutExtension);\n        if (Directory.Exists(previousDir))\n            Directory.Delete(previousDir, true);\n        Directory.Move(tempDirPath, previousDir);\n\n        // Finally, last step: the original file gets replaced with a tag to let us know it's a multi-file\n        File.WriteAllText(filename, \"DIZ-MULTIFILE:version=1\");\n    }\n}\n\npublic class FileSplitWriter\n{\n    public int FileCount { get; private set; } = -1;\n\n    protected StreamWriter? Sw = null;\n    public string OutputDir { get; }\n\n    public FileSplitWriter(string outputDir)\n    {\n        OutputDir = outputDir;\n    }\n\n    public void OutputSplitFiles(string content)\n    {\n        // super-hacky stringsplit. we are NOT parsing XML, just splitting up into files whenever we see any of the following.\n        // the entire point of this is for better git merging, so, optimize for that use case.\n        // this is a little brittle and specific to the Diz2.0 file format as of 2023. if it gives you any trouble, don't rely on it.\n        var tokens = new List<string>\n        {\n            \"<Data\",\n            \"<Regions\",\n            \"<Comments\",\n            \"<Labels\",\n            \"<RomBytes\",    // SUPERHACK WARNING: overlaps with <RomBytesOutputFormatSettings too.\n            \"\\r\\n;pos=\",    // we'll include the newline here to make it easier when merging later.\n                            // otherwise, a newline would be required at the END of each text file fragment and if removed would blow things up.\n                            // a newline will still be required at the top of the file, but, if removed it'll happen to work out.\n            \"</RomBytes>\",\n            \"</Data>\"\n        };\n\n        // Step 1: Find indices of every token\n        var tokenPositions = new List<(int index, string token)>();\n        foreach (var token in tokens)\n        {\n            var index = 0;\n\n            while ((index = content.IndexOf(token, index, StringComparison.Ordinal)) != -1)\n            {\n                tokenPositions.Add((index, token));\n                index += token.Length;\n            }\n        }\n\n        // Sort our found positions\n        tokenPositions = tokenPositions.OrderBy(item => item.index).ToList();\n\n        // Step 2: Output each range to a separate file\n        var lastIndex = 0;\n        SwitchToNewOutputFile(\"\");\n\n        for (var i = 0; i < tokenPositions.Count; i++)\n        {\n            var endIndex = tokenPositions[i].index;\n            Sw!.Write(content[lastIndex..endIndex]);\n\n            var token = tokenPositions[i].token;\n            lastIndex = endIndex + token.Length;\n\n            var filePostfix = SanitizeFileChars(token);\n            SwitchToNewOutputFile(filePostfix);\n\n            Sw!.Write(token);\n        }\n\n        // Write remaining content to the last file, if any\n        if (lastIndex < content.Length)\n        {\n            Sw!.Write(content[lastIndex..]);\n        }\n\n        if (Sw == null)\n            return;\n\n        Sw.Flush();\n        Sw.Close();\n        Sw = null;\n    }\n\n    private static string SanitizeFileChars(string nextToken) =>\n        Regex.Replace(nextToken, \"[^a-zA-Z0-9_.]\", \"\");\n\n    private void SwitchToNewOutputFile(string filePostfix = \"\")\n    {\n        if (Sw != null)\n        {\n            Sw.Flush();\n            Sw.Close();\n        }\n\n        FileCount++;\n        var fullFilename = CreateFullFilename(filePostfix);\n        Sw = new StreamWriter(fullFilename);\n    }\n\n    private string CreateFullFilename(string filePostfix)\n    {\n        var finalPostfix = filePostfix.Length > 0 ? $\"_{filePostfix}\" : \"\";\n        return Path.Combine(OutputDir, $\"{FileCount:D5}_save{finalPostfix}.txt\");\n    }\n}"
  },
  {
    "path": "Diz.Core/serialization/FileByteProviderSingleFile.cs",
    "content": "﻿#nullable enable\nusing System.IO;\n\nnamespace Diz.Core.serialization;\n\npublic class FileByteProviderSingleFile : IFileByteProvider\n{\n    public byte[] ReadAllBytes(string filename) => \n        File.ReadAllBytes(filename);\n    \n    public void WriteBytes(string filename, byte[] data) => \n        File.WriteAllBytes(filename, data);\n}"
  },
  {
    "path": "Diz.Core/serialization/ImportSettings.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.ComponentModel;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\n\nnamespace Diz.Core.serialization\n{\n    public interface IRomImportSettings : INotifyPropertyChanged\n    {\n        public RomMapMode RomMapMode  { get; }\n        public int RomSettingsOffset  { get; }\n        public RomSpeed RomSpeed  { get; }\n        public Dictionary<int, Label> InitialLabels  { get; }\n        public Dictionary<int, FlagType> InitialHeaderFlags { get; }\n        public IReadOnlyList<byte> RomBytes { get; }\n        public string RomFilename { get; }\n    } \n    \n    public class ImportRomSettings : IRomImportSettings\n    {\n        public RomMapMode RomMapMode\n        {\n            get => mode;\n            set => this.SetField(PropertyChanged, ref mode, value);\n        }\n        private RomMapMode mode;\n        \n        public int RomSettingsOffset => \n            RomUtil.GetRomSettingOffset(RomMapMode);\n        \n        public RomSpeed RomSpeed\n        {\n            get => romSpeed;\n            set => this.SetField(PropertyChanged, ref romSpeed, value);\n        }\n        private RomSpeed romSpeed;\n        \n        /// <summary>\n        /// List of Rom offsets (not SNES addresses) for labels for initial vector tables/etc\n        /// </summary>\n        public Dictionary<int, Label> InitialLabels\n        {\n            get => initialLabels;\n            set => this.SetField(PropertyChanged, ref initialLabels, value);\n        }\n        private Dictionary<int, Label> initialLabels = new();\n\n        public Dictionary<int, FlagType> InitialHeaderFlags\n        {\n            get => initialHeaderFlags;\n            set => this.SetField(PropertyChanged, ref initialHeaderFlags, value);\n        }\n        private Dictionary<int, FlagType> initialHeaderFlags = new();\n\n        public List<byte> RomBytes\n        {\n            get => romBytes;\n            set => this.SetField(PropertyChanged, ref romBytes, value);\n        }\n        private List<byte> romBytes;\n        IReadOnlyList<byte> IRomImportSettings.RomBytes => romBytes;\n\n        public string RomFilename\n        {\n            get => romFilename;\n            set => this.SetField(PropertyChanged, ref romFilename, value);\n        }\n        private string romFilename;\n\n        public event PropertyChangedEventHandler PropertyChanged;\n    }\n}"
  },
  {
    "path": "Diz.Core/serialization/ProjectFileManager.cs",
    "content": "﻿#nullable enable\n\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing Diz.Core.model;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.util;\nusing ExtendedXmlSerializer;\nusing ExtendedXmlSerializer.Configuration;\n\nnamespace Diz.Core.serialization;\n\npublic interface IProjectSerializer\n{\n    ProjectOpenResult Load(byte[] rawBytes);\n    byte[] Save(Project project);\n    IMigrationRunner MigrationRunner { get; }\n}\n\npublic interface IProjectXmlSerializer : IProjectSerializer\n{\n    public delegate void SerializeEvent(IProjectXmlSerializer projectXmlSerializer, ProjectXmlSerializer.Root rootElement);\n    public event SerializeEvent BeforeSerialize;\n    public event SerializeEvent AfterDeserialize;\n}\n\npublic interface IAddRomDataCommand\n{\n    public bool ShouldProjectCartTitleMatchRomBytes { get; set; }\n    public ProjectXmlSerializer.Root? Root { get; set; }\n    public Func<string, string>? GetNextRomFileToTry { get; set; }\n    public IMigrationRunner? MigrationRunner { get; set; }\n\n    public void TryReadAttachedProjectRom();\n}\n\npublic interface IFileByteReader\n{\n    byte[] ReadAllBytes(string filename);\n}\n\npublic interface IFileByteWriter\n{\n    void WriteBytes(string filename, byte[] data);\n}\n\npublic interface IFileByteProvider : IFileByteReader, IFileByteWriter;\n\npublic interface IProjectFileUserPrefs\n{\n    void LoadUserPreferences(string projectFilename, Project project);\n    void SaveUserPreferences(string projectFilename, ProjectUserSettings projectUserSettings);\n}\n\nclass ProjectFileUserPrefs : IProjectFileUserPrefs\n{\n    public void LoadUserPreferences(string projectFilename, Project project)\n    {\n        try\n        {\n            var userPrefsFilename = BuildUserPrefsFilenameFromProjectName(projectFilename);\n            var userPrefsXmlStr = LoadUserPrefsXmlStrFromFile(userPrefsFilename);\n\n            var userPrefsXmlSerializer = GetUserPrefsXmlSerializerConfigContainer();\n            project.ProjectUserSettings = userPrefsXmlSerializer.Create().Deserialize<ProjectUserSettings>(userPrefsXmlStr);\n        }\n        catch (Exception e)\n        {\n            // we don't really care about the user prefs that much. just ignore and use the defaults if there's an issue.\n            Console.WriteLine($\"Warning: failed to load user project prefs, ignoring them: associated with: {projectFilename}: {e.Message}\");\n        }\n    }\n\n    public void SaveUserPreferences(string projectFilename, ProjectUserSettings projectUserSettings)\n    {\n        try\n        {\n            var userPrefsFilename = BuildUserPrefsFilenameFromProjectName(projectFilename);\n            var userPrefsXmlSerializer = GetUserPrefsXmlSerializerConfigContainer();\n            var userPrefsXmlStr = userPrefsXmlSerializer.Create().Serialize(projectUserSettings);\n            File.WriteAllText(userPrefsFilename, userPrefsXmlStr);\n        }\n        catch (Exception e)\n        {\n            // we don't really care about the user prefs that much. just ignore and use the defaults if there's an issue.\n            Console.WriteLine($\"Warning: failed to save user project prefs, ignoring: associated with: {projectFilename}: {e.Message}\");\n        }\n    }\n\n    private static string LoadUserPrefsXmlStrFromFile(string filename) => \n        Encoding.UTF8.GetString(File.ReadAllBytes(filename));\n\n    private static string BuildUserPrefsFilenameFromProjectName(string projectFilename)\n    {\n        var baseFilenameNoExtension = Path.GetFileNameWithoutExtension(projectFilename);\n        return Path.Combine(Path.GetDirectoryName(projectFilename)!, baseFilenameNoExtension + \".dizprefs\");\n    }\n\n    private static IConfigurationContainer GetUserPrefsXmlSerializerConfigContainer() =>\n        new ConfigurationContainer()\n            .Type<ProjectUserSettings>()\n            .EnableImplicitTyping();\n}\n\n// NOP version (for unit tests or if you don't care about user prefs)\npublic class StubProjectFileUserPrefs : IProjectFileUserPrefs {\n    public void LoadUserPreferences(string projectFilename, Project project) {}\n    public void SaveUserPreferences(string projectFilename, ProjectUserSettings projectUserSettings) {}\n}\n\n\npublic class ProjectFileManager(\n    Func<IProjectXmlSerializer> projectXmlSerializerCreate,\n    Func<IAddRomDataCommand> addRomDataCommandCreate,\n    Func<string, IFileByteProvider> fileByteProviderFactory,\n    IProjectFileUserPrefs? userPrefsLoader = null\n) : IProjectFileManager\n{\n    public Func<string, string>? RomPromptFn { get; set; } = null;\n\n    public ProjectOpenResult Open(string filename)\n    {\n        Trace.WriteLine(\"Opening Project START\");\n\n        var (serializer, openResult) = Deserialize(filename);\n        VerifyIntegrityDeserialized(openResult.Root);\n        OnPostProjectDeserialized(filename, openResult.Root, serializer);\n\n        Trace.WriteLine(\"Opening Project END\");\n        return openResult;\n    }\n\n    private void OnPostProjectDeserialized(string filename, ProjectXmlSerializer.Root xmlProjectSerializedRoot, IProjectSerializer serializer)\n    {\n        // 1. housekeeping\n        var newlyOpenedProject = xmlProjectSerializedRoot.Project;\n        \n        newlyOpenedProject.ProjectFileName = filename;\n        newlyOpenedProject.Session = new ProjectSession(newlyOpenedProject, filename);\n        \n        // 2. need to load the user prefs next BECAUSE the Rom filename is stored there, and we need it for the next step\n        userPrefsLoader?.LoadUserPreferences(filename, newlyOpenedProject);\n\n        // 3. at this stage, 'Data' is populated with everything EXCEPT the actual ROM bytes.\n        // It would be easy to store the ROM bytes in the save file, but, for copyright reasons,\n        // we leave it out.\n        //\n        // So now, with all our metadata loaded successfully, we now open the .smc file on disk\n        // and marry the original rom's bytes with all of our metadata loaded from the project file.\n        var romAddCmd = addRomDataCommandCreate();\n        Debug.Assert(romAddCmd != null);\n        \n        romAddCmd.Root = xmlProjectSerializedRoot;\n        romAddCmd.GetNextRomFileToTry = RomPromptFn;\n        romAddCmd.MigrationRunner = serializer.MigrationRunner;\n\n        romAddCmd.TryReadAttachedProjectRom();\n    }\n\n    private static void VerifyIntegrityDeserialized(ProjectXmlSerializer.Root xmlProjectSerializedRoot)\n    {\n        var data = xmlProjectSerializedRoot.Project.Data;\n        Debug.Assert(data.Labels != null && data.Comments != null);\n        Debug.Assert(data.RomBytes is { Count: > 0 });\n    }\n\n    private (IProjectSerializer serializer, ProjectOpenResult projectOpenResult) Deserialize(string filename)\n    {\n        var projectFileBytes = ReadProjectFileBytes(filename);\n        var serializer = GetSerializerForFormat(projectFileBytes);\n        var projectOpenResult = DeserializeWith(serializer, projectFileBytes ?? []);\n        return (serializer, projectOpenResult);\n    }\n\n    private byte[]? ReadProjectFileBytes(string filename)\n    {\n        var fileByteIo = CreateFileBytesProvider(filename);\n\n        var projectFileBytes = fileByteIo.ReadAllBytes(filename);\n\n        if (IsLikelyCompressed(filename))\n            projectFileBytes = Util.TryUnzip(projectFileBytes);\n            \n        return projectFileBytes;\n    }\n    \n    private static bool IsDirectoryBasedProject(string filename) => \n        Path.GetExtension(filename).Equals(\".dizdir\", StringComparison.InvariantCultureIgnoreCase);\n\n    private IFileByteProvider CreateFileBytesProvider(string filename)\n    {\n        var projectSaveType = IsDirectoryBasedProject(filename) ? \"Multiple\" : \"Single\";\n        return fileByteProviderFactory(projectSaveType);\n    }\n\n    public static bool IsBinaryFileFormat(byte[]? data)\n    {\n        if (data == null)\n            return false;\n        \n        try {\n            return !ProjectSerializer.DizWatermark.Where((t, i) => data[i + 1] != (byte)t).Any();\n        } catch (Exception) {\n            return false;\n        }\n    }\n\n    private IProjectSerializer GetSerializerForFormat(byte[]? data)\n    {\n        if (IsBinaryFileFormat(data))\n        {\n            #if ENABLE_LEGACY_BINARY_SERIALIZER\n                return new BinarySerializer();\n            #else\n            throw new InvalidDataException(\n                \"Legacy binary serializer is no longer supported (use an older version of Diz to update your save file\");\n            #endif\n        }\n\n        return projectXmlSerializerCreate();\n    }\n\n    private static readonly string[] UncompressedExtensions = {\n        \".dizraw\", \n        \".dizdir\"\n    };\n\n    private static bool IsLikelyCompressed(string filename) => \n        !UncompressedExtensions.Any(ext => ext.Equals(Path.GetExtension(filename), StringComparison.InvariantCultureIgnoreCase));\n\n    public string? Save(Project project, string filename)\n    {\n        try\n        {\n            // Everything saves in XML format from here on out.\n            // Binary format is deprecated.\n            Save(project, filename, projectXmlSerializerCreate());\n        }\n        catch (Exception ex)\n        {\n            return ex.Message;\n        }\n\n        return null;\n    }\n\n    private void Save(Project project, string filename, IProjectSerializer serializer)\n    {\n        var data = DoSave(project, filename, serializer);\n        \n        // failed to create the save bytes to write to disk\n        if (data == null)\n            return;\n\n        var fileByteIo = CreateFileBytesProvider(filename);\n        fileByteIo.WriteBytes(filename, data);\n        \n        project.ProjectFileName = filename;\n        \n        if (project.Session == null) \n            return;\n        project.Session.ProjectFileName = project.ProjectFileName;\n        \n        // only do this at the VERY END once we know the file is safe on disk\n        project.Session.UnsavedChanges = false;\n        \n        \n        // finally... unrelated to the main project file, let's save the user preferences \n        // seperately.  this is for project-specific things that should be persisted to disk, but not shared with multiple users.\n        // i.e. this file should be added to the project's .gitignore.\n        userPrefsLoader?.SaveUserPreferences(filename, project.ProjectUserSettings);\n    }\n\n    private byte[]? DoSave(Project project, string filename, IProjectSerializer serializer)\n    {\n        var data = SerializeWith(project, serializer);\n\n        if (IsLikelyCompressed(filename))\n            data = Util.TryZip(data);\n\n        return data;\n    }\n\n    #region Hooks\n    protected virtual ProjectOpenResult DeserializeWith(IProjectSerializer serializer, byte[] rawBytes) => \n        serializer.Load(rawBytes);\n\n    protected virtual byte[] SerializeWith(Project project, IProjectSerializer serializer) => \n        serializer.Save(project);\n\n    #endregion\n}\n\npublic interface IProjectFileManager\n{\n    /// <summary>\n    /// A function that the project file manager calls if it needs to ask the user\n    /// to locate a ROM that matches a project file. If null, the check is skipped.\n    /// </summary>\n    Func<string, string>? RomPromptFn { get; set; }\n    \n    /// <summary>\n    /// Open a file from disk\n    /// </summary>\n    /// <param name=\"filename\"></param>\n    /// <returns>Info about how it went, plus the data</returns>\n    ProjectOpenResult Open(string filename);\n    \n    /// <summary>\n    /// Save a project to disk\n    /// </summary>\n    /// <param name=\"project\"></param>\n    /// <param name=\"filename\"></param>\n    /// <returns>error msg. null if succeeded, otherwise a string with the error messages of why it can't open.</returns>\n    string? Save(Project project, string filename);\n}"
  },
  {
    "path": "Diz.Core/serialization/ProjectSerializer.cs",
    "content": "﻿using System.Diagnostics;\nusing System.IO;\nusing Diz.Core.model;\nusing Diz.Core.serialization.xml_serializer;\n\nnamespace Diz.Core.serialization;\n\npublic abstract class ProjectSerializer\n{\n    protected ProjectSerializer(IMigrationRunner migrationRunner)\n    {\n        MigrationRunner = migrationRunner;\n    }\n\n    public const string DizWatermark = \"DiztinGUIsh\";\n\n    public abstract byte[] Save(Project project);\n    public abstract ProjectOpenResult Load(byte[] rawBytes);\n\n    public void SaveToFile(Project project, string filename)\n    {\n        File.WriteAllBytes(filename, Save(project));\n    }\n        \n    public ProjectOpenResult LoadFromFile(string filename)\n    {\n        return Load(File.ReadAllBytes(filename));\n    }\n\n    protected static void DebugVerifyProjectEquality(Project project1, Project project2, bool deepCut = true)\n    {\n        if (deepCut)\n        {\n            for (var i = 0; i < project1.Data.RomBytes.Count; ++i)\n            {\n                Debug.Assert(project1.Data.RomBytes[i].EqualsButNoRomByte(project2.Data.RomBytes[i]));\n            }\n\n            Debug.Assert(project1.Data.RomBytes.Equals(project2.Data.RomBytes));\n            Debug.Assert(project1.Data.Equals(project2.Data));\n        }\n        Debug.Assert(project1.Equals(project2));\n    }\n\n    // caution: don't cache, these may not be reliable for resetting themselves each time they run.\n    public IMigrationRunner MigrationRunner { get; }\n}"
  },
  {
    "path": "Diz.Core/serialization/binary_serializer_old/BinarySerializer.cs",
    "content": "﻿#if ENABLE_LEGACY_BINARY_SERIALIZER\n\nusing System;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Linq;\nusing Diz.Core.model;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.model.snes;\nusing Diz.Core.util;\n\n// IMPORTANT NOTE:\n// This serializer is compact, but it's deprecated in favor of the XML serializer, which is way easier\n// to make changes to and deal with backwards compatibility.\n//\n// This is only here for loading older files saved in this format, it shouldn't be used for anything new going forward.\n\nnamespace Diz.Core.serialization.binary_serializer_old;\n\ninternal class BinarySerializer : ProjectSerializer\n{\n    public const int HeaderSize = 0x100;\n    private const int LatestFileFormatVersion = 2;\n\n    public override byte[] Save(Project project)\n    {\n        const int versionToSave = LatestFileFormatVersion;\n        var data = SaveVersion(project, versionToSave);\n\n        var everything = new byte[HeaderSize + data.Length];\n        everything[0] = versionToSave;\n        ByteUtil.StringToNullTermByteArray(DizWatermark).CopyTo(everything, 1);\n        data.CopyTo(everything, HeaderSize);\n\n        return data;\n    }\n\n    public override (ProjectXmlSerializer.Root xmlRoot, string warning) Load(byte[] rawBytes)\n    {\n        var (project, warning, version) = LoadProject(rawBytes);\n\n        // the binary serializer versions start at \"1\" and go up to a max of 99.\n        // XML serializers pick up at 100 and go upwards from there.\n        Debug.Assert(version < ProjectXmlSerializer.FirstSaveFormatVersion);\n            \n        // have to pack this into the new XML root structure.\n        return (new ProjectXmlSerializer.Root\n        {\n            Project = project,\n            SaveVersion = version,\n            Watermark = DizWatermark,\n        }, warning);\n    }\n    private (Project project, string warning, byte version) LoadProject(byte[] data)\n    {\n        if (!IsBinaryFileFormat(data))\n            throw new InvalidDataException(\"This is not a binary serialized project file!\");\n\n        var version = data[0];\n        ValidateProjectFileVersion(version);\n\n        var project = new Project {\n            Data = new Data()\n        };\n\n#if DIZ_3_BRANCH\n            project.Session = new ProjectSession(project)\n            {\n                UnsavedChanges = false\n            };\n#endif\n\n        // version 0 needs to convert PC to SNES for some addresses\n        ByteUtil.AddressConverter converter = address => address;\n        if (version == 0)\n            converter = project.Data.ConvertPCtoSnes;\n\n        // read mode, speed, size\n        var mode = (RomMapMode) data[HeaderSize];\n        var speed = (RomSpeed) data[HeaderSize + 1];\n            \n#if !DIZ_3_BRANCH\n        project.Data.RomMapMode = mode;\n        project.Data.RomSpeed = speed;\n#endif\n            \n        var size = ByteUtil.ConvertByteArrayToInt32(data, HeaderSize + 2);\n\n        // read internal title\n        var pointer = HeaderSize + 6;\n        project.InternalRomGameName = ByteUtil.ReadStringFromByteArray(data, pointer, RomUtil.LengthOfTitleName);\n        pointer += RomUtil.LengthOfTitleName;\n\n        // read checksums\n        project.InternalCheckSum = ByteUtil.ConvertByteArrayToUInt32(data, pointer);\n        pointer += 4;\n\n        // read full filepath to the ROM .sfc file\n        while (data[pointer] != 0)\n            project.AttachedRomFilename += (char) data[pointer++];\n        pointer++;\n\n#if DIZ_3_BRANCH\n            project.Data.InitializeEmptyRomMapping(size, mode, speed);\n#else\n        project.Data.RomBytes.Create(size);\n#endif\n\n        for (int i = 0; i < size; i++) project.Data.SetDataBank(i, data[pointer + i]);\n        for (int i = 0; i < size; i++)\n            project.Data.SetDirectPage(i, data[pointer + size + i] | (data[pointer + 2 * size + i] << 8));\n        for (int i = 0; i < size; i++) project.Data.SetXFlag(i, data[pointer + 3 * size + i] != 0);\n        for (int i = 0; i < size; i++) project.Data.SetMFlag(i, data[pointer + 4 * size + i] != 0);\n        for (int i = 0; i < size; i++) project.Data.SetFlag(i, (FlagType) data[pointer + 5 * size + i]);\n        for (int i = 0; i < size; i++) project.Data.SetArchitecture(i, (Architecture) data[pointer + 6 * size + i]);\n        for (int i = 0; i < size; i++) project.Data.SetInOutPoint(i, (InOutPoint) data[pointer + 7 * size + i]);\n        pointer += 8 * size;\n\n        ReadLabels(project, data, ref pointer, converter, version >= 2);\n        ReadComments(project, data, ref pointer, converter);\n\n#if !DIZ_3_BRANCH\n        if (project.Session != null) \n            project.Session.UnsavedChanges = false;\n#endif\n\n        var warning = \"\";\n        if (version != LatestFileFormatVersion)\n        {\n            warning = \"This project file is in an older format.\\n\" +\n                      \"You may want to back up your work or 'Save As' in case the conversion goes wrong.\\n\" +\n                      \"The project file will be untouched until it is saved again.\";\n        }\n\n        return (project, warning, version);\n    }\n\n#if ALLOW_OLD_SAVE_FORMATS\n        private static void SaveStringToBytes(string str, ICollection<byte> bytes)\n        {\n            // TODO: combine with Util.StringToByteArray() probably.\n            if (str != null) {\n                foreach (var c in str) {\n                    bytes.Add((byte)c);\n                }\n            }\n            bytes.Add(0);\n        }\n#endif\n\n    private void VoidTheWarranty()\n    {\n        // comment this out only if you are an expert and know what you're doing. Binary serialization is deprecated.\n        //\n        // How did you even get here, dawg? #yolo\n        throw new NotSupportedException(\"Binary serializer saving is OLD, please use the XML serializer instead.\");\n    }\n\n    [SuppressMessage(\"ReSharper\", \"UnusedParameter.Local\")]\n    private byte[] SaveVersion(Project project, int version)\n    {\n        VoidTheWarranty();\n            \n#if ALLOW_OLD_SAVE_FORMATS\n            ValidateSaveVersion(version);\n\n            int size = project.Data.GetRomSize();\n            byte[] romSettings = new byte[31];\n\n            // save these two\n            romSettings[0] = (byte)project.Data.RomMapMode;\n            romSettings[1] = (byte)project.Data.RomSpeed;\n\n            // save the size, 4 bytes\n            ByteUtil.IntegerIntoByteArray((uint)size, romSettings, 2);\n\n            var romName = project.Data.CartridgeTitleName;\n            romName.ToCharArray().CopyTo(romSettings, 6);\n\n            var romChecksum = project.Data.RomCheckSumsFromRomBytes;\n            BitConverter.GetBytes(romChecksum).CopyTo(romSettings, 27);\n\n            // TODO put selected offset in save file\n\n            // save all labels ad comments\n            List<byte> label = new(), comment = new();\n            var allLabels = project.Data.Labels;\n            var allComments = project.Data.Comments;\n\n            ByteUtil.AppendIntegerToByteList((uint)allLabels.Count, label);\n            foreach (var pair in allLabels)\n            {\n                ByteUtil.AppendIntegerToByteList((uint)pair.Key, label);\n\n                SaveStringToBytes(pair.Value.Name, label);\n                if (version >= 2)\n                {\n                    SaveStringToBytes(pair.Value.Comment, label);\n                }\n            }\n\n            ByteUtil.AppendIntegerToByteList((uint)allComments.Count, comment);\n            foreach (KeyValuePair<int, Comment> pair in allComments)\n            {\n                ByteUtil.AppendIntegerToByteList((uint)pair.Key, comment);\n                SaveStringToBytes(pair.Value.Text, comment);\n            }\n\n            // save current Rom full path - \"c:\\whatever\\someRom.sfc\"\n            var romLocation = ByteUtil.StringToNullTermByteArray(project.AttachedRomFilename);\n\n            var data = new byte[romSettings.Length + romLocation.Length + 8 * size + label.Count + comment.Count];\n            romSettings.CopyTo(data, 0);\n            for (int i = 0; i < romLocation.Length; i++) data[romSettings.Length + i] = romLocation[i];\n\n            var readOps = new Func<int, byte>[]\n            {\n                i => (byte)project.Data.GetDataBank(i),\n                i => (byte)project.Data.GetDataBank(i),\n                i => (byte)project.Data.GetDirectPage(i),\n                i => (byte)(project.Data.GetDirectPage(i) >> 8),\n                i => (byte)(project.Data.GetXFlag(i) ? 1 : 0),\n                i => (byte)(project.Data.GetMFlag(i) ? 1 : 0),\n                i => (byte)project.Data.GetFlag(i),\n                i => (byte)project.Data.GetArchitecture(i),\n                i => (byte)project.Data.GetInOutPoint(i),\n            };\n\n            void ReadOperation(int startIdx, int whichOp)\n            {\n                if (whichOp <= 0 || whichOp > readOps.Length)\n                    throw new ArgumentOutOfRangeException(nameof(whichOp));\n\n                var baseidx = startIdx + whichOp * size;\n                var op = readOps[whichOp];\n                for (var i = 0; i < size; i++)\n                {\n                    data[baseidx + i] = op(i);\n                }\n            }\n\n            for (var i = 0; i < readOps.Length; ++i)\n            {\n                var start = romSettings.Length + romLocation.Length;\n                ReadOperation(start, i);\n            }\n            \n            // ???\n            label.CopyTo(data, romSettings.Length + romLocation.Length + 8 * size);\n            comment.CopyTo(data, romSettings.Length + romLocation.Length + 8 * size + label.Count);\n            // ???\n\n            return data;\n#endif\n        return null;\n    }\n\n#if ALLOW_OLD_SAVE_FORMATS\n        private static void ValidateSaveVersion(int version) {\n            if (version < 1 || version > LatestFileFormatVersion) {\n                throw new ArgumentException($\"Saving: Invalid save version requested for saving: {version}.\");\n            }\n        }\n#endif\n\n    private static void ValidateProjectFileVersion(int version)\n    {\n        if (version > LatestFileFormatVersion)\n        {\n            throw new ArgumentException(\n                \"This DiztinGUIsh file uses a newer file format! You'll need to download the newest version of DiztinGUIsh to open it.\");\n        }\n\n        if (version < 0)\n        {\n            throw new ArgumentException($\"Invalid project file version detected: {version}.\");\n        }\n    }\n\n    private void ReadComments(Project project, byte[] bytes, ref int pointer, ByteUtil.AddressConverter converter)\n    {\n        const int stringsPerEntry = 1;\n        pointer += ByteUtil.ReadStringsTable(bytes, pointer, stringsPerEntry, converter, \n            (offset, strings) =>\n            {\n                Debug.Assert(strings.Length == 1);\n                project.Data?.AddComment(offset, strings[0], true);\n            });\n    }\n\n    private void ReadLabels(Project project, byte[] bytes, ref int pointer, ByteUtil.AddressConverter converter, bool readAliasComments)\n    {\n        var stringsPerEntry = readAliasComments ? 2 : 1;\n        pointer += ByteUtil.ReadStringsTable(bytes, pointer, stringsPerEntry, converter,\n            (offset, strings) =>\n            {\n                Debug.Assert(strings.Length == stringsPerEntry);\n                var label = new Label\n                {\n                    Name = strings[0],\n                    Comment = strings.ElementAtOrDefault(1)\n                };\n                project.Data.Labels.AddLabel(offset, label, true);\n            });\n    }\n}\n\n#endif"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/MigrationRunner.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.serialization.xml_serializer\n{\n    // - Integrity checks run and version numbers are compatible for migrations\n    // - any XML-based migrations (from ExtendedXmlSerializer etc) have already run\n    public class MigrationRunner : IMigrationRunner\n    {\n        // constraints:\n        // - every item in this list must have it's target version >= the previous version (sorted)\n        // - multiple of the same version# ARE allowed\n        // - items of equal version# will be applied in the order they're in the list\n        // - caller must keep this sorted\n        public List<IMigration> Migrations { get; init; } = [];\n\n        IReadOnlyList<IMigration> IMigrationRunner.Migrations => Migrations;\n        \n        // when run, we'll filter out any migrations not between the Start and Target ranges\n        public int StartingSaveVersion { get; set; }\n        public int TargetSaveVersion { get; set; }\n\n        public MigrationRunner(IEnumerable<IMigration> migrations) : this() \n        {\n            if (migrations != null)\n                Migrations = migrations.ToList();\n        }\n\n        public MigrationRunner()\n        {\n        }\n\n        private void PreMigrationChecks()\n        {\n            var currentVersion = int.MinValue;\n            foreach (var migration in Migrations)\n            {\n                if (migration == null)\n                    throw new DizMigrationException(\n                        \"internal: all migrations must be non-null\");\n\n                if (migration.AppliesToSaveVersion < currentVersion)\n                    throw new DizMigrationException(\n                        \"internal: all migrations must >= other migrations in the sequence\");\n\n                EnsureVersionIsSameOrNext(migration.AppliesToSaveVersion, currentVersion);\n\n                currentVersion = migration.AppliesToSaveVersion;\n            }\n            \n            if (StartingSaveVersion > TargetSaveVersion)\n                throw new DizMigrationException(\n                    \"internal: starting migration version is greater than target version\");\n        }\n\n        private void RunAllMigrations(Action<IMigration> applyAction)\n        {\n            PreMigrationChecks();\n            \n            if (StartingSaveVersion == TargetSaveVersion)\n                return;\n            \n            int lastStartingVersionApplied = -1, lastStartingVersionConsidered = StartingSaveVersion;\n            \n            foreach(var migration in Migrations)\n            {\n                if (ValidateVersionForNext(migration.AppliesToSaveVersion, lastStartingVersionConsidered))\n                {\n                    applyAction(migration);\n                    lastStartingVersionApplied = migration.AppliesToSaveVersion;\n                }\n\n                lastStartingVersionConsidered = migration.AppliesToSaveVersion;\n            }\n\n            FinalChecks(lastStartingVersionApplied + 1);\n        }\n\n        private void FinalChecks(int finalVersionReached)\n        {\n            if (finalVersionReached != TargetSaveVersion)\n                throw new DizMigrationException(\n                    $\"migration failed. we were trying to upgrade to v{TargetSaveVersion}, but the final version# achieved was v{finalVersionReached}\");\n        }\n\n        // throw an exception if anything is out of bounds\n        // return true if we should apply this migration version\n        private bool ValidateVersionForNext(int proposedVersion, int currentVersion)\n        {\n            if (proposedVersion < StartingSaveVersion)\n                return false;\n            \n            if (!EnsureVersionIsSameOrNext(proposedVersion, currentVersion))\n                throw new DizMigrationException(\n                    $\"internal: migration out of sequence. version {proposedVersion} not valid here. needed to upgrade from {currentVersion}\");\n\n            return proposedVersion >= StartingSaveVersion && proposedVersion < TargetSaveVersion;\n        }\n\n        // you can apply any mitigation that's at or 1 above our current version.\n        // this is so multiple items of the same version number can be applied\n        private static bool EnsureVersionIsSameOrNext(int proposedVersion, int currentVersion) => \n            proposedVersion == currentVersion || \n            proposedVersion == currentVersion + 1;\n\n        public void OnLoadingBeforeAddLinkedRom(IAddRomDataCommand romAddCmd)\n        {\n            RunAllMigrations(migration => migration.OnLoadingBeforeAddLinkedRom(romAddCmd));\n        }\n\n        public void OnLoadingAfterAddLinkedRom(IAddRomDataCommand romAddCmd)\n        {\n            RunAllMigrations(migration => migration.OnLoadingAfterAddLinkedRom(romAddCmd));\n        }\n    }\n}\n\npublic class DizMigrationException : Exception\n{\n    public DizMigrationException([CanBeNull] string message, [CanBeNull] Exception innerException) : base(message, innerException)\n    {\n    }\n\n    public DizMigrationException([CanBeNull] string message) : base(message)\n    {\n    }\n}"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/PostSerializeMigrations.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Diz.Core.serialization.xml_serializer\n{\n    public interface IMigrationEvents\n    {\n        // add migrations to hook in various places in the code as needed.\n        // example: something to pre-process incoming XML text, or modify the XML deserializer before it's used\n\n        void OnLoadingBeforeAddLinkedRom(IAddRomDataCommand romAddCmd) { }\n        void OnLoadingAfterAddLinkedRom(IAddRomDataCommand romAddCmd) { }\n    }\n    \n    public interface IMigration : IMigrationEvents\n    {\n        // Each Migration has a unique version#, and will upgrade data in that version#\n        // to the next version#.\n        public int AppliesToSaveVersion { get; }\n    }\n\n    public interface IMigrationRunner : IMigrationEvents\n    {\n        public IReadOnlyList<IMigration> Migrations { get; }\n        int StartingSaveVersion { get; set; }\n        int TargetSaveVersion { get; set; }\n    }\n}"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/ProjectXMLSerializer.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Text;\nusing System.Xml;\nusing System.Xml.Linq;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing ExtendedXmlSerializer;\nusing ExtendedXmlSerializer.Configuration;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.serialization.xml_serializer;\n\npublic class ProjectOpenResult\n{\n    public class Result\n    {\n        public List<string> Warnings { get; set; } = new();\n        \n        /// <summary>\n        /// The project version# of this file on disk before loaded it.\n        /// </summary>\n        public int ProjectFileOriginalVersion { get; set; } = -1;\n\n        /// <summary>\n        /// The project version# of the data after we loaded it from disk\n        /// It may be different from ProjectFileOriginalVersion if we upgraded it.\n        /// </summary>\n        public int ProjectFileUpgradedToVersion { get; set; } = -1;\n    }\n\n    /// <summary>\n    /// Contains metadata from the operation of opening the file (not the file contents).\n    /// Contains things like non-fatal warnings, etc, anything we might want communicate to the user about the act\n    /// Of Loading this project from disk.\n    /// </summary>\n    public Result OpenResult { get; } = new();\n    \n    /// <summary>\n    /// The actual data for the root object that gets deserialized from the project file. Contains the Project\n    /// </summary>\n    public ProjectXmlSerializer.Root Root { get; set; }\n}\n\npublic class ProjectXmlSerializer : ProjectSerializer, IProjectXmlSerializer\n{\n    public const int FirstSaveFormatVersion = 100;   // NEVER CHANGE THIS ONE.\n\n    // increment this if you change the XML file format\n    // history:\n    // - 100: original XML version for Diz 2.0, from <= 2020. [see notes below about earlier now-removed binary save format]\n    // - 101: no structure changes but, japanese chars in SNES header cartridge title were being saved\n    //        incorrectly, so, allow project XMLs to load IF we can fix up the bad data.\n    // - 102: added comments support to the special RomBytes data section.\n    //        bumped RomBytes data format from v200 (initial) to v201.\n    // - 103: added \"context\" fields to labels, added regions (no migrations required)\n    // - 104: added \"ExportSeparateFile\" field to regions (no migrations required)\n    public const int LatestSaveFormatVersion = 104;  //  REMEMBER: IF YOU CHANGE THIS YOU MUST ADD A NEW IMigration ENTRY IN RegisterMigrations()\n    \n    // About older project save formats from ancient Diz 1.0:\n    // The older binary savefile format BEFORE v100 in Diz 1.0 is removed, and modern Diz can't open them anymore.\n    // To deal with opening one of these ancient saves:\n    // - grab one of the old Diz 2.0 versions from the Github Releases page\n    //   [around 2021 era likely works. try Diz v2.1.1.2 \"ish\" - like https://github.com/IsoFrieze/DiztinGUIsh/releases/tag/v2.2.1.2]\n    //   that supported both the old Binary Serialization format, and .diz XML file formats >v100.\n    // - open old project file, re-save as XML-based file format.\n    // - open with latest Diz and it should be able to read any XML-based file format.\n    //\n    // it is unlikely anyone using Diz these days has one of these older file formats that\n    // hasn't already been converted, so shouldn't be a big deal.\n    // reach out in Discord if you need help with this.\n    \n    // changes as we run the migrations\n    public int CurrentSaveFormatVersion { get; } = LatestSaveFormatVersion;\n\n    // update this if we are dropping support for ancient save formats.\n    public const int EarliestSupportedSaveFormatVersion = FirstSaveFormatVersion;\n    \n    [CanBeNull] public event IProjectXmlSerializer.SerializeEvent BeforeSerialize;\n    [CanBeNull] public event IProjectXmlSerializer.SerializeEvent AfterDeserialize;\n    \n    \n    public class Root\n    {\n        // XML serializer specific metadata, top-level deserializer.\n        // This is unique to JUST the XML serializer, doesn't affect any other types of serializers.\n        // i.e. there is no global 'save format version' number, it's serializer-specific.\n        //\n        // NOTE: Please try and keep 'Root' unchanged and as generic as possible.  It's way better\n        // to change 'Project'\n        public int SaveVersion { get; set; } = -1;\n        public string Watermark { get; set; }\n        public string Extra1 { get; set; } = \"\"; // reserved for future use\n        public string Extra2 { get; set; } = \"\"; // reserved for future use\n\n        // The actual project itself. Almost any change you want to make should go in here.\n        public Project Project { get; set; }\n    };\n\n    \n    private readonly IXmlSerializerFactory xmlSerializerFactory;\n    public ProjectXmlSerializer(IXmlSerializerFactory xmlSerializerFactory, IMigrationRunner migrationRunner = null, int migrateLoadedXmlToVersion = -1) : base(migrationRunner)\n    {\n        if (migrateLoadedXmlToVersion != -1)\n            CurrentSaveFormatVersion = migrateLoadedXmlToVersion;\n        \n        this.xmlSerializerFactory = xmlSerializerFactory;\n    }\n\n    public override byte[] Save(Project project)\n    {\n        // Wrap the project in a top-level root element with some info about the XML file\n        // format version. Note that each serializer has its own implementation of storing this metadata\n        var rootElement = new Root\n        {\n            SaveVersion = CurrentSaveFormatVersion,\n            Watermark = DizWatermark,\n            Project = project\n        };\n        \n        // one silly thing. we'll re-sort labels so they show up in the output file in order.\n        project.Data.Labels.SortLabels();\n\n        BeforeSerialize?.Invoke(this, rootElement);\n\n        var serializerConfig = GetSerializerConfig(project.ProjectSettings.RomBytesOutputFormatSettings);\n        \n        var xmlStr = serializerConfig.Create().Serialize(\n            new XmlWriterSettings {Indent = true},\n            rootElement);\n\n        return Encoding.UTF8.GetBytes(xmlStr);\n    }\n\n    private IConfigurationContainer GetSerializerConfig(\n        [CanBeNull] RomBytesOutputFormatSettings romBytesOutputFormat = null        // only used when saving\n        ) => \n        xmlSerializerFactory.GetSerializer(romBytesOutputFormat);\n    \n    public override ProjectOpenResult Load(byte[] projectFileRawXmlBytes)\n    {\n        // Note: Migrations not yet written for XML itself. ExtendedXmlSerializer has support for this\n        // if we need it, put it in a new MigrationRunner.SetupMigrateXml() or similar.\n\n        var xmlStr = Encoding.UTF8.GetString(projectFileRawXmlBytes);\n        var versionOnDisk = RunPreDeserializeIntegrityChecks(xmlStr);\n\n        MigrationRunner.StartingSaveVersion = versionOnDisk;\n        MigrationRunner.TargetSaveVersion = CurrentSaveFormatVersion;\n\n        var root = DeserializeProjectXml(xmlStr);\n        RunIntegrityChecks(root.SaveVersion, root.Watermark);\n            \n        AfterDeserialize?.Invoke(this, root);\n\n        var projectOpenResult = new ProjectOpenResult\n        {\n            Root = root,\n            OpenResult =\n            {\n                ProjectFileOriginalVersion = versionOnDisk,\n                ProjectFileUpgradedToVersion = CurrentSaveFormatVersion,   \n            }\n        };\n        \n        if (versionOnDisk != CurrentSaveFormatVersion)\n        {\n            // this isn't necessarily anything that's an issue, but, we definitely want to tell the user.\n            projectOpenResult.OpenResult.Warnings.Add($\"Diz project file format on disk was older: [file version={versionOnDisk}].\" +\n                                                      $\" When this project is next saved, it will automatically be upgraded to the latest format [file version={CurrentSaveFormatVersion}].\" +\n                                                      $\" To be safe, you take a moment to make a backup copy of your project files before saving or exporting, just in case.\");\n        }\n        \n        return projectOpenResult;\n    }\n\n    // finally. this is the real deal.\n    private Root DeserializeProjectXml(string xmlStr) => \n        GetSerializerConfig().Create().Deserialize<Root>(xmlStr);\n\n    // return the save file version# detected in the raw data\n    private int RunPreDeserializeIntegrityChecks(string rawXml)\n    {\n        // run this check before opening with our real serializer. read a minimal part of the XML\n        // manually to verify the root element looks sane.\n        var xDoc = XDocument.Parse(rawXml);\n        var xRoot = xDoc.Root;\n            \n        var saveVersionStr = xRoot?.Attribute(\"SaveVersion\")?.Value;\n        var waterMarkStr = xRoot?.Attribute(\"Watermark\")?.Value;\n            \n        if (string.IsNullOrEmpty(saveVersionStr))\n            throw new InvalidDataException(\"SaveVersion attribute missing on root element\");\n\n        var saveVersion = int.Parse(saveVersionStr);\n\n        RunIntegrityChecks(saveVersion, waterMarkStr);\n\n        return saveVersion;\n    }\n\n    // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local\n    private void RunIntegrityChecks(int saveVersion, string watermark)\n    {\n        if (watermark != DizWatermark)\n            throw new InvalidDataException(\n                \"This file doesn't appear to be a valid DiztinGUIsh XML file (missing/invalid watermark element in XML)\");\n            \n        if (saveVersion > CurrentSaveFormatVersion)\n            throw new InvalidDataException(\n                $\"The save file version '{saveVersion}' is newer than is supported in this version of DiztinGUIsh\"+\n                $\", cancelling project open. (we only support save versions <= {CurrentSaveFormatVersion}).\"+\n                \" Please check if there is an update for DiztinGUIsh in order to open this file.\");\n\n        // Apply any migrations here for older save file formats. Right now,\n        // there aren't any because we're on the first revision.\n        // The XML serialization might be fairly forgiving of most kinds of changes,\n        // so you may not have to write migrations unless properties are renamed or deleted.\n        if (saveVersion < EarliestSupportedSaveFormatVersion)\n        {\n            throw new InvalidDataException(\n                $\"The save file version is from an older version of DiztinGUIsh and can't be imported with this newer version. Try using an older version to bring it up to date and re-import here again.\" +\n                \"Please check for an upgraded release of DiztinGUIsh, it should be able to open this file.\"+\n                $\"(Save file version of loaded project: '{saveVersion}', we are expecting version {CurrentSaveFormatVersion}.)\");\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/RepeaterCompression.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\n\nnamespace Diz.Core.serialization.xml_serializer\n{\n    public static class RepeaterCompression\n    {\n        public static void Decompress(ref List<string> lines)\n        {\n            var output = new List<string>();\n\n            foreach (var line in lines)\n            {\n                if (!line.StartsWith(\"r\"))\n                {\n                    output.Add(line);\n                    continue;\n                }\n\n                var split = line.Split(' ');\n                if (split.Length != 3)\n                    throw new InvalidDataException(\"Invalid repeater command\");\n\n                var count = int.Parse(split[1]);\n                for (var i = 0; i < count; ++i)\n                {\n                    output.Add(split[2]);\n                }\n            }\n\n            lines = output;\n        }\n\n        public static void Compress(ref List<string> lines)\n        {\n            if (lines.Count < 8)\n                return; // forget it, too small to care.\n\n            var output = new List<string>();\n\n            var lastLine = lines[0];\n            var consecutive = 1;\n\n            // adjustable, just pick something > 8 or it's not worth the optimization.\n            // we want to catch large consecutive blocks of data.\n            const int minNumberRepeatsBeforeWeBother = 8;\n\n            var totalLinesDebug = 0;\n\n            for (var i = 1; i < lines.Count; ++i)\n            {\n                var line = lines[i];\n                Debug.Assert(!line.StartsWith(\"r\"));\n\n                var different = line != lastLine;\n                var finalLine = i == lines.Count - 1;\n\n                if (!different)\n                {\n                    consecutive++;\n\n                    if (!finalLine)\n                        continue;\n                }\n\n                if (consecutive >= minNumberRepeatsBeforeWeBother)\n                {\n                    // replace multiple repeated lines with one new statement\n                    output.Add($\"r {consecutive.ToString()} {lastLine}\");\n                }\n                else\n                {\n                    // output 1 or more copies of the last line\n                    // this is also how we print single lines too\n                    output.AddRange(Enumerable.Repeat(lastLine, consecutive).ToList());\n                }\n\n                if (finalLine && different)\n                {\n                    output.Add(line);\n                    totalLinesDebug++;\n                }\n\n                totalLinesDebug += consecutive;\n\n                lastLine = line;\n                consecutive = 1;\n            }\n\n            Debug.Assert(totalLinesDebug == lines.Count);\n\n            lines = output;\n        }\n    }\n}\n"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/RomByteEncoding.cs",
    "content": "﻿// #define EXTRA_DEBUG_CHECKS\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Text;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\n\nnamespace Diz.Core.serialization.xml_serializer\n{\n    public class RomByteEncoding\n    {\n        private static readonly IReadOnlyDictionary<char, FlagType> FlagEncodeTable = new Dictionary<char, FlagType>()\n        {\n            {'U', FlagType.Unreached},\n\n            {'+', FlagType.Opcode},\n            {'.', FlagType.Operand},\n\n            {'G', FlagType.Graphics},\n            {'M', FlagType.Music},\n            {'X', FlagType.Empty},\n            {'T', FlagType.Text},\n\n            {'A', FlagType.Data8Bit},\n            {'B', FlagType.Data16Bit},\n            {'C', FlagType.Data24Bit},\n            {'D', FlagType.Data32Bit},\n\n            {'E', FlagType.Pointer16Bit},\n            {'F', FlagType.Pointer24Bit},\n            {'H', FlagType.Pointer32Bit},\n        };\n\n        private readonly StringBuilder cachedPadSb = new(LineMaxLen);\n\n        private const int LineMaxLen = 9;\n\n        // note: performance-intensive function. be really careful when adding stuff here.\n        public RomByte DecodeRomByte(string line)\n        {\n            var input = PrepLine(line);\n\n            var newByte = new RomByte();\n\n            var flagTxt = input[0];\n            var otherFlags1 = Fake64Encoding.DecodeHackyBase64(input[1]);\n            newByte.DataBank = ByteUtil.ByteParseHex2(input[2], input[3]);\n            newByte.DirectPage = (int)ByteUtil.ByteParseHex4(input[4], input[5], input[6], input[7]);\n            newByte.Arch = (Architecture)(ByteUtil.ByteParseHex1(input[8]) & 0x3);\n\n            #if EXTRA_DEBUG_CHECKS\n            Debug.Assert(Fake64Encoding.EncodeHackyBase64(otherFlags1) == o1_str);\n            #endif\n\n            newByte.XFlag = ((otherFlags1 >> 2) & 0x1) != 0;\n            newByte.MFlag = ((otherFlags1 >> 3) & 0x1) != 0;\n            newByte.Point = (InOutPoint)((otherFlags1 >> 4) & 0xF);\n\n            var found = false;\n            foreach (var e in FlagEncodeTable)\n            {\n                if (e.Key != flagTxt)\n                    continue;\n\n                newByte.TypeFlag = e.Value;\n                found = true;\n                break;\n            }\n            \n            if (!found)\n                throw new InvalidDataException(\"Unknown FlagType\");\n\n            return newByte;\n        }\n\n        // perf note: re-uses a cached StringBuilder for subsequent runs\n        private StringBuilder PrepLine(string line)\n        {\n            if (cachedPadSb.Length == 0)\n                cachedPadSb.Append(\"000000000\"); // any 9 chars\n\n            // light decompression. ensure our line is always 9 chars long.\n            // if any characters are missing, pad them with zeroes\n            //\n            // perf: string.PadRight() is simpler but too slow, so do it by hand\n            var inSize = line.Length;\n            for (var i = 0; i < LineMaxLen; ++i)\n            {\n                cachedPadSb[i] = i < inSize ? line[i] : '0';\n            }\n\n            Debug.Assert(cachedPadSb.Length == LineMaxLen);\n\n            return cachedPadSb;\n        }\n        \n        private static readonly char[] HexChars = \"0123456789ABCDEF\".ToCharArray();\n        \n        [ThreadStatic]\n        private static char[] _outBuffer;\n        \n        public static string EncodeByte(RomByte instance)\n        {\n            // use a custom formatter here to save disk space, while still being easy to merge with git.\n            // there are a LOT of ROMBytes.\n            // despite that we're still going for:\n            // 1) text only for slightly human readability\n            // 2) mergability in git/etc\n            //\n            // some of this can be unpacked further to increase readability without\n            // hurting the filesize too much. figure out what's useful.\n            //\n            // sorry, I know the encoding looks insane and weird and specific.  this reduced my\n            // save file size from 42MB to less than 13MB\n            //\n            // ALSO: this function is extremely CPU performance-intensive.\n            _outBuffer ??= new char[9];\n\n            // NOTE: must be uppercase letter or \"=\" or \"-\"\n            // if you add things here, make sure you understand the compression settings above.\n            var flagTxt = ' ';\n            foreach (var e in FlagEncodeTable)\n            {\n                if (e.Value != instance.TypeFlag) \n                    continue;\n                \n                flagTxt = e.Key;\n                break;\n            }\n\n            if (flagTxt == ' ')\n                throw new InvalidDataException(\"Unknown FlagType\");\n\n            // max 6 bits if we want to fit in 1 base64 ASCII digit\n            var otherFlags1 = (byte)(\n                (instance.XFlag ? 1 : 0) << 2 | // 1 bit\n                (instance.MFlag ? 1 : 0) << 3 | // 1 bit\n                (byte)instance.Point << 4   // 4 bits\n                                            // LEAVE OFF THE LAST 2 BITS. it'll mess with the base64 below\n            );\n            // reminder: when decoding, have to cut off all but the first 6 bits\n            var o1Str = Fake64Encoding.EncodeHackyBase64(otherFlags1);\n            Debug.Assert(Fake64Encoding.DecodeHackyBase64(o1Str) == otherFlags1);\n\n            if (!instance.XFlag && !instance.MFlag && instance.Point == 0)\n            {\n                Debug.Assert(o1Str == '0'); // sanity\n            }\n\n            // this is basically going to be \"0\" almost 100% of the time.\n            // we'll put it on the end of the string so it's most likely not output\n            var otherFlags2 = (byte)(\n                (byte)instance.Arch << 0 // 2 bits\n            );\n\n            // ordering: put DB and D on the end, they're more likely to be zero and compressible\n            \n            // Fill the output buffer.\n            // we're not using strings or stringbuffers here since this code is called in such a tight loop.\n            // also avoid using .ToString() or allocating memory in here as much as we can help it.\n            var pos = 0;\n            _outBuffer[pos++] = flagTxt;\n            _outBuffer[pos++] = o1Str;\n            \n            \n            // Append DataBank (X2)\n            _outBuffer[pos++] = HexChars[instance.DataBank >> 4];\n            _outBuffer[pos++] = HexChars[instance.DataBank & 0xF];\n    \n            // Append DirectPage (X4)\n            _outBuffer[pos++] = HexChars[(instance.DirectPage >> 12) & 0xF];\n            _outBuffer[pos++] = HexChars[(instance.DirectPage >> 8) & 0xF];\n            _outBuffer[pos++] = HexChars[(instance.DirectPage >> 4) & 0xF];\n            _outBuffer[pos++] = HexChars[instance.DirectPage & 0xF];\n            \n            // Append otherFlags2\n            _outBuffer[pos] = HexChars[otherFlags2];\n            \n            // early: light compression: chop off all trailing zeroes.\n            // this alone saves a giant amount of space in the output file\n            //\n            // Find the last non-zero character\n            var lastNonZero = 8;\n            while (lastNonZero > 0 && _outBuffer[lastNonZero] == '0')\n                lastNonZero--;\n            \n            // Create the final string (only up to the last non-zero character)\n            return new string(_outBuffer, 0, lastNonZero + 1);\n            \n            // future compression but dilutes readability:\n            // if type is opcode or operand with same flags, combine those into 1 type.\n            // i.e. take an opcode with 3 operands, represent it using one digit in the file.\n            // instead of \"=---\", we swap with \"+\" or something. small optimization.\n        }\n    }\n}\n"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/RomBytesXMLSerializer.cs",
    "content": "﻿// define to do some extra checking as we save the data out\n// #define EXTRA_DEBUG_CHECKS\n\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Diz.Core.model;\nusing ExtendedXmlSerializer.ContentModel;\nusing ExtendedXmlSerializer.ContentModel.Format;\n\n// everything else in the savefiles is straight up normal XML, but,\n// the amount of bytes for the ROM metadata can be huge.\n// by using a custom serializer for this one section, we can cut a 4MB rom savefile down from ~45MB to ~1.5MB\n//\n// This uses some hacky compression methods designed to preserve:\n// 1) newlines\n// 2) slight human readability for merging, so projects can be collaborated on in source control\n// 3) using pattern substitution tables that won't change from PC to PC.\n//\n// The easiest thing would be use zlib (and we can use it with the output of the entire XML if we want)\n// but, for projects with multiple collaborators using diztinGUIsh, mergability in text/git/editors/etc\n// is a must.  We aim for a tradeoff between decent compression and some small semblance of human readability.\n//\n// It's not.. super-pretty code, but it compresses well.\nnamespace Diz.Core.serialization.xml_serializer;\n\n[TypeConverter(typeof(ExpandableObjectConverter))]\npublic class RomBytesOutputFormatSettings\n{\n    // note: some of these will only do anything the NEXT time a file is saved to disk.\n    // this struct will also be serialized with the project.\n\n    // These settings control some (ad-hoc / hacky) compression for output text.\n    // The goal is *purely* to reduce the size of the final XML file, but do so in a way that is still [somewhat] mergeable\n    // for team collaboration using tools like 'git merge' etc.\n    //\n    // These settings ONLY affect data that's being serialized (Save project) and whether we attempt to compress it a bit more.\n    // For data that's being deserialized (during Load Project), we need to always be able to deal with these encodings.\n    //\n    // For the maximually mergeable version (but, at the cost of slightly larger filesize), leave both OFF.\n        \n        \n    /// <summary>\n    /// Run-length compression for the \"RomBytes\" special data section in the output file.\n    /// Find output lines that are identical, and use run-length encoding to mark how many times a line should be repeated.\n    /// Diz projects tend to be CRAMMED FULL of duplicate lines, especially for huge unbroken sections of content/graphics/music/etc.\n    /// Enabling this is a huge win for filesize, but, at the expensive of merge-friendliness\n    /// It's a pretty good choice to leave this ON by default.\n    ///\n    /// This is only used when saving, not loading.\n    /// </summary>\n    /// <value>\n    /// <c>true</c> if the group block should be compressed on save; otherwise, <c>false</c>.\n    /// </value>\n    [DisplayName(\"Project file: Enable Run-Length Encoding compression\")]\n    [Description(\n        \"ADVANCED: Defaults to ON. If disabled, don't apply run-length encoding to your .diz/.dizraw save file. \" +\n        \"Disable this if you want to keep all lines in your save file 1:1 with bytes from the original ROM.\" +\n        \"The only reason you care about this is if you are working on a team and want to merge .dizraw files together in git more easily.\" +\n        \"This option will take the save file and increase it to about 10x the size on average (still not a big deal though)\"\n    )]\n    public bool CompressGroupBlock { get; set; } = true;\n        \n    /// <summary>\n    /// CompressUsingTable1: Take the output lines, and run some substitutions for the most common patterns we see in Diz files\n    /// (as of a few analyzed in 2021). This is like an incredibly crappy gzip-style encoding, but, optimized to still output readable text that \n    /// preserves line breaks (so it can still be merged as a text file with external tools).\n    /// \n    /// This won't change the # of output lines (just their content), so it's pretty merge-friendly.\n    /// Humans won't be able to read the lines well though, it'll just look like \"base64\"-ish gibberish.\n    /// \n    /// Still, a pretty nice win and solid tradeoff of filesize and human text merging friendliness.\n    ///\n    /// This is only used when saving, not loading.\n    /// </summary>\n    /// <value>\n    /// <c>true</c> if the text should be substituted for common patterns on save\n    /// </value>\n    [DisplayName(\"Project file: Enable per-line substitution\")]\n    [Description(\n        \"ADVANCED: Defaults to ON. Disable to make the .dizraw file a bit more readable, at the expense of file size. No real reason to turn this off.\"\n    )]\n    public bool CompressUsingTable1 { get; set; } = true;\n\n    /// <summary>\n    /// Optional. if not zero, then every N output lines in the XML file's RomBytes section we'll output a comment\n    /// with the ROM offset.  this is 100% optional, it's purely for humans and merge tools to be able to deal with\n    /// less search area when doing tricky merges.\n    /// Comments are ignored on load.\n    /// This is only used when saving, not loading.\n    /// Smaller numbers here make easier merges, but, increase the filesize.\n    /// 0x4000 is a nice tradeoff between negligible file increase and much better merge friendliness \n    /// </summary>\n    [DisplayName(\"Project file: Break up lines in .diz file with offset comments\")]\n    [Description(\n        \"If non-zero, in the .diz/.dizraw project file, add comments every N bytes in the .dizraw file. \" +\n        \"Lower this number to increase mergability or diff viewing when usig git/other text merge tools.\"\n    )]\n    public int InsertCommentIntoOutputLinesEveryNBytes { get; set; } = 0x800;\n        \n    public override string ToString() => \"\";\n}\n\n    \nsealed class RomBytesSerializer : ISerializer<RomBytes>\n{\n    // let the outer XML class do the heavy lifting on versioning.\n    // but, let's add one here just because this specific class is complex.\n    // CHANGE THIS if you make breaking changes to this file (please try not to).\n    // if you DO, make sure to add a migration, or, mark that one isn't needed.\n    // history of data format changes:\n    //  version     description\n    //  -------     ----------------\n    //  200         initial data format\n    //  201         add ability to add comments\n    private const int MaxSupportedDataFormatVersion = 201;\n        \n    // this is the oldest data format we'll attempt to read. If it's older, it's not supported.\n    private const int OldestAllowedDataFormatVersion = 200;\n\n    public static RomBytesSerializer Default { get; } = new();\n\n    public RomBytesOutputFormatSettings FormatSettings = new();\n        \n    public int NumTasksToUse { get; init; } = 5; // seems like the sweet spot\n\n    public RomBytes Get(IFormatReader parameter)\n    {\n        var allLines = ReadMainDataRaw(parameter.Content());\n        var romBytes = DecodeAllBytes(allLines);\n        return FinishRead(romBytes);\n    }\n\n    private RomByte[] DecodeAllBytes(IReadOnlyList<string> allLines)\n    {\n        if (NumTasksToUse == 1)\n            return DecodeRomBytes(allLines, 0, allLines.Count);\n\n        var tasks = new List<Task<RomByte[]>>(NumTasksToUse);\n\n        var nextIndex = 0;\n        var workListCount = allLines.Count / NumTasksToUse;\n\n        for (var t = 0; t < NumTasksToUse; ++t)\n        {\n            if (t == NumTasksToUse - 1)\n                workListCount = allLines.Count - nextIndex;\n\n            tasks.Add(CreateDecodeRomBytesTask(allLines, nextIndex, workListCount));\n\n            nextIndex += workListCount;\n        }\n\n        var continuation = Task.WhenAll(tasks);\n        continuation.Wait();\n        return continuation.Result.SelectMany(i => i).ToArray();\n    }\n\n    private static Task<RomByte[]> CreateDecodeRomBytesTask(IReadOnlyList<string> allLines, int nextIndex, int workListCount)\n    {\n        // ReSharper disable once AccessToStaticMemberViaDerivedType\n        return Task<RomByte[]>.Run(() => DecodeRomBytes(allLines, nextIndex, workListCount));\n    }\n\n    private static RomByte[] DecodeRomBytes(IReadOnlyList<string> allLines, int startIndex, int count)\n    {\n        // perf: allocate all at once, don't use List.Add() one at a time\n        var romBytes = new RomByte[count];\n        var romByteEncoding = new RomByteEncoding();\n        var i = 0;\n\n        try\n        {\n            while (i < count)\n            {\n                var line = allLines[startIndex + i];\n                romBytes[i] = romByteEncoding.DecodeRomByte(line);\n                ++i;\n            }\n        }\n        catch (Exception ex)\n        {\n            ex.Data.Add(\"ParseLineNum\", \"Near line# \" + (startIndex + i));\n            throw;\n        }\n\n        return romBytes;\n    }\n\n    private static RomBytes FinishRead(RomByte[] romBytes)\n    {\n        var romBytesOut = new RomBytes();\n        romBytesOut.SetFrom(romBytes);\n        return romBytesOut;\n    }\n\n    private static List<string> ReadMainDataRaw(string allLines)\n    {\n        // first line is the header\n        var (lines, options) = ReadHeader(allLines);\n        CheckForCompatibleVersion(options);\n            \n        var encodedWithCompressTable1 = options.Exists(s => s == \"compress_table_1\");\n        var encodedWithCompressGroupBlocks = options.Exists(s => s == \"compress_groupblocks\");\n            \n        // everything after this is the actual data in the file.\n        // no matter what, it should be newline-delimited.\n        // IMPORTANT: always apply these operations in the reverse order of the SAVE function.\n        if (encodedWithCompressTable1)\n            SubstitutionCompression.DecodeCompression_Table1(ref lines);\n            \n        if (encodedWithCompressGroupBlocks)\n            RepeaterCompression.Decompress(ref lines);\n            \n        return lines\n            .Select(line => line.Contains(';') ? line[..line.IndexOf(';')].TrimEnd() : line) // remove any comments\n            .Where(line => !string.IsNullOrWhiteSpace(line)) // remove any blank lines (including newly blanked lines because they used to have a comment in them)\n            .ToList();\n    }\n\n    private static (List<string> lines, List<string> options) ReadHeader(string allLines)\n    {\n        // clean... this...\n        var lines = allLines.Split(new char[] {'\\n'}, 3).ToList();\n        var options = lines[1].Split(new char[] {','}).ToList();\n        lines = lines[2].Split(new char[] {'\\n'}).ToList();\n        if (lines[lines.Count - 1] == \"\")\n            lines.RemoveAt(lines.Count - 1);\n        return (lines, options);\n    }\n\n    private static void CheckForCompatibleVersion(IEnumerable<string> options)\n    {\n        try\n        {\n            var versionNum = ParseVersionNumFromOptions(options);\n\n            // if we hit this, we're trying to open a file saved with a newer version of Diz.\n            // we should bail. Try to avoid things that break this.\n            if (versionNum > MaxSupportedDataFormatVersion)\n                throw new InvalidDataException(\n                    $\"Newer ROMBytes section format detected: version={versionNum}. This version of distinguish only supports data table formats up to {MaxSupportedDataFormatVersion}.\");\n                \n            // if we're at the correct data version, all good.\n            if (versionNum == MaxSupportedDataFormatVersion) \n                return;\n                \n            // otherwise, we are opening a file saved with an older data format and may need to convert input data to our newer format.\n            // \n            // dev notes: when the data version changes, please write migrations here that convert old data to new data\n            // (or, leave a note that nothing breaking changed and no migration is actually needed)\n                \n            // too old, we're dead\n            if (versionNum < OldestAllowedDataFormatVersion)\n                throw new InvalidDataException(\n                    $\"Older ROMBytes section format detected: version={versionNum},\"\n                    + \" but, we don't know how to convert it to the newer format\"\n                    + \" (this is probably not legit and is likely a bug, please report it).\" +\n                    $\" This version of distinguish only supports ROMBytes section formats up to version={MaxSupportedDataFormatVersion}.\"\n                );\n                \n            // we're at an older version, but, we can run the migrations below to upgrade to newer versions \n                    \n            // ReSharper disable once ConditionIsAlwaysTrueOrFalse\n            if (versionNum == 200)\n            {\n                // v200 was the original data format.\n                // to upgrade v200 to v201, we do: nothing :)\n                // v201 simply adds support for ignoring comments on lines. v200 files will never have comments.\n                // older versions of diz will choke on comments. our version will simply ignore comments\n                versionNum = 201; // nothing to migrate, we're done, mark us as being at version 201.\n            }\n                    \n            // at the end of our migrations, our data must have been changed (if necessary) and the version# must match the latest.\n            // ReSharper disable once ConditionIsAlwaysTrueOrFalse\n            if (versionNum != MaxSupportedDataFormatVersion)\n            {\n                throw new InvalidDataException(\n                    $\"BUG: (please report)\" \n                    + \" ROMBytes data migration from older version failed: we did update to version={versionNum},\"\n                    + $\" but it should have reached CurrentDataFormatVersion={MaxSupportedDataFormatVersion}.\"\n                );\n            }\n        }\n        catch (Exception ex)\n        {\n            throw new InvalidDataException($\"Data table loader: Version error: {ex.Message}\");\n        }\n    }\n\n    private static int ParseVersionNumFromOptions(IEnumerable<string> options)\n    {\n        var versionOption = options.SingleOrDefault(s => s.Contains(\"version:\"));\n\n        if (versionOption == null)\n        {\n            throw new InvalidDataException(\n                $\"Exactly 1 'version' tag must be in options, unable to continue\");\n        }\n\n        var split = versionOption.Split(':');\n        Debug.Assert(split.Length == 2);\n        if (!int.TryParse(split[1], out var versionNum))\n            throw new InvalidDataException(\n                $\"Couldn't parse version # from RomBytes version tag\");\n        return versionNum;\n    }\n\n    public void Write(IFormatWriter writer, RomBytes instance)\n    {\n        var options = new List<string>\n        {\n            $\"version:{MaxSupportedDataFormatVersion}\",\n        };\n\n        var lines = new List<string>(capacity: instance.Count);\n            \n        // generate all text lines.\n        for (var romOffset = 0; romOffset < instance.Count; romOffset++)\n        {\n            // the important stuff: generate the real data, this is the entire point:\n            var rb = instance[romOffset];\n            var encodedTxt = RomByteEncoding.EncodeByte(rb);\n            lines.Add(encodedTxt);\n                \n            // completely optional: output a comment every N bytes. this is purely to make it easier for humans to read and \n            // use merge tools like git to figure out where they might be in the file. otherwise, it's basically impossible to know where you are in the file\n            // if you're just looking at it in in a text editor.\n            // note: we want to have these comment breaks FIRST so that the RLE compression in CompressGroupBlock stops at these boundaries.\n            // i.e. no matter what wacky stuff the RLE compression does, the comments will remain at constant in any output file.\n            // hopefully these comments serve as good 'anchors' in git merge algorithms.\n            if (FormatSettings.InsertCommentIntoOutputLinesEveryNBytes > 0 && romOffset > 0 && romOffset % FormatSettings.InsertCommentIntoOutputLinesEveryNBytes == 0)\n            {\n                // at this lower level we do have all the rom bytes but we don't have bank/mapping info.\n                // so, try to keep InsertCommentIntoOutputLinesEveryNBytes a multiple of something that lines up on every bank length on the SNES.\n                lines.Add($\";pos={romOffset:X6}\");\n            }\n\n            // debug check, optional:\n#if EXTRA_DEBUG_CHECKS\n                var decoded = romByteEncoding.DecodeRomByte(encodedTxt);\n                Debug.Assert(decoded.EqualsButNoRomByte(rb));\n#endif\n        }\n\n        // all our output data is finished and we could write it to the file right now and it would be valid. \n        // HOWEVER, we can run some postprocessors for transforming the data (via some light compression, adding comments, etc)\n        // for the sake of merge-friendliness or other uses.\n        PostProcessOutputData(options, ref lines);\n\n        writer.Content($\"\\n{string.Join(\",\", options)}\\n\");\n\n        foreach (var line in lines)\n        {\n            writer.Content(line);\n            writer.Content(\"\\n\");\n        }\n    }\n\n    private void PostProcessOutputData(ICollection<string> options, ref List<string> lines)\n    {\n        if (FormatSettings.CompressGroupBlock)\n        {\n            options.Add(\"compress_groupblocks\");\n            RepeaterCompression.Compress(ref lines);\n        }\n\n        if (FormatSettings.CompressUsingTable1)\n        {\n            options.Add(\"compress_table_1\");\n            SubstitutionCompression.EncodeCompression_Table1(ref lines);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/SubstitutionCompression.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\n\nnamespace Diz.Core.serialization.xml_serializer\n{\n    public static class SubstitutionCompression\n    {\n        private class CompressionEntry\n        {\n            public string LongTextPattern;\n            public string ShortTextToEncode;\n        }\n\n        private static readonly List<CompressionEntry> Table1 = new()\n        {\n            new CompressionEntry {LongTextPattern = \"0001E\", ShortTextToEncode=\"ZQ\"},\n            new CompressionEntry {LongTextPattern = \"B0001\", ShortTextToEncode=\"Zq\"},\n            new CompressionEntry {LongTextPattern = \"C0001\", ShortTextToEncode=\"ZX\"},\n            new CompressionEntry {LongTextPattern = \"B7E\", ShortTextToEncode=\"Zx\"},\n            new CompressionEntry {LongTextPattern = \"07F01\", ShortTextToEncode=\"ZY\"},\n            new CompressionEntry {LongTextPattern = \"0001D\", ShortTextToEncode=\"Zy\"},\n            new CompressionEntry {LongTextPattern = \"C7E\", ShortTextToEncode=\"ZZ\"},\n            new CompressionEntry {LongTextPattern = \"07E\", ShortTextToEncode=\"Zz\"},\n            new CompressionEntry {LongTextPattern = \"00001\", ShortTextToEncode=\"ZS\"},\n            new CompressionEntry {LongTextPattern = \"0001\", ShortTextToEncode=\"Zs\"},\n        };\n\n        public static void DecodeCompression_Table1(ref List<string> lines)\n        {\n            for (var i = 0; i < lines.Count; ++i)\n            {\n                // shouldn't matter much but, apply in reverse to ensure it's done the same\n                // way as the encoding process\n                foreach (var e in Table1.Reverse<CompressionEntry>())\n                {\n                    lines[i] = lines[i].Replace(e.ShortTextToEncode, e.LongTextPattern);\n                }\n            }\n        }\n\n        public static void EncodeCompression_Table1(ref List<string> lines)\n        {\n            // kind of a manually made / crappy huffman table encoding type thing.\n            // this is no great work of genius, more just some cheap hacks to reduce filesize.\n            // these are based on one half-assembled ROM i was using and probably don't \n            // universally work perfectly.  I expect at some point we could collect a bunch of projects\n            // and make a Table2, add that here without having to bump the file version.\n            for (var i = 0; i < lines.Count; ++i)\n            {\n                foreach (var e in Table1)\n                {\n                    Debug.Assert(!lines[i].Contains(e.ShortTextToEncode));\n                    lines[i] = lines[i].Replace(e.LongTextPattern, e.ShortTextToEncode);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/XMLSerializerSupport.cs",
    "content": "﻿using Diz.Core.model;\nusing ExtendedXmlSerializer.Configuration;\n\nnamespace Diz.Core.serialization.xml_serializer;\n\npublic interface IXmlSerializerFactory\n{\n    public IConfigurationContainer GetSerializer(RomBytesOutputFormatSettings romBytesOutputFormat);\n}"
  },
  {
    "path": "Diz.Core/serialization/xml_serializer/XmlSerializerFactory.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\nusing ExtendedXmlSerializer;\nusing ExtendedXmlSerializer.Configuration;\nusing ExtendedXmlSerializer.ContentModel.Conversion;\nusing ExtendedXmlSerializer.ContentModel.Format;\nusing ExtendedXmlSerializer.ExtensionModel.Instances;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.serialization.xml_serializer;\n\npublic class InvalidCharStrippingConverter : IConverter<string>\n{\n    public string Parse(string data) => data; // Handle reading - no changes needed\n\n    public string Format(string instance)\n    {\n        if (string.IsNullOrEmpty(instance)) return instance;\n        \n        // Remove all invalid XML 1.0 characters\n        // Valid XML 1.0 characters are:\n        // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]\n        var result = new StringBuilder(instance.Length);\n        foreach (var c in instance.Where(IsValidXmlChar)) {\n            result.Append(c);\n        }\n        \n        return result.ToString();\n    }\n    \n    private static bool IsValidXmlChar(char c)\n    {\n        return c == 0x09 ||          // Tab\n               c == 0x0A ||          // Line Feed\n               c == 0x0D ||          // Carriage Return\n               (c >= 0x20 && c <= 0xD7FF) ||      // Basic Multilingual Plane\n               (c >= 0xE000 && c <= 0xFFFD);      // Private Use Area and others\n    }\n\n    public bool IsSatisfiedBy(TypeInfo parameter)\n    {\n        return parameter.AsType() == typeof(string);\n    }\n}\n\npublic class XmlSerializerFactory(\n    IDataFactory dataFactory,\n    Func<IDataFactory, XmlSerializerFactory.SnesDataInterceptor> snesDataInterceptor)\n    : IXmlSerializerFactory\n{\n    public IConfigurationContainer GetSerializer([CanBeNull] RomBytesOutputFormatSettings romBytesOutputFormat)\n    {\n        var romBytesSerializer = new RomBytesSerializer\n        {\n            FormatSettings = romBytesOutputFormat\n        };\n\n        return new ConfigurationContainer()\n\n            .WithDefaultMonitor(new SerializationMonitor())\n\n            .Type<Project>()\n            .Member(x => x.ProjectUserSettings).Ignore()\n            .Member(x=>x.InternalRomGameName).Register(new InvalidCharStrippingConverter())\n            \n\n            .Type<RomBytes>()\n            .Register().Serializer().Using(romBytesSerializer)\n        \n            .Type<Data>()\n            .WithInterceptor(snesDataInterceptor(dataFactory))\n            .Member(x => x.LabelsSerialization)\n            .Name(\"Labels\")\n        \n            .EnableImplicitTyping(typeof(ContextMapping))\n\n            .Type<Label>()\n            \n#if DIZ_3_BRANCH\n                .Name(\"L\")\n                .Member(x => x.Comment).Name(\"Cmt\").EmitWhen(text => !string.IsNullOrEmpty(text))\n                .Member(x => x.Name).Name(\"V\").EmitWhen(text => !string.IsNullOrEmpty(text))\n#endif\n            .EnableImplicitTyping()\n        \n            .Type<IAnnotationLabel>()\n            .WithInterceptor(AnnotationLabelInterceptor.Default)\n\n            .UseOptimizedNamespaces()\n            .UseAutoFormatting();\n#if DIZ_3_BRANCH\n                .EnableReferences()\n#endif\n    }\n\n    /// <summary>\n    /// Generic serialization monitor. Use this to hook into key events, debug, report progress, etc.\n    /// </summary>\n    private class SerializationMonitor : ISerializationMonitor\n    {\n        public void OnSerializing(IFormatWriter writer, object instance)\n        {\n                \n        }\n\n        public void OnSerialized(IFormatWriter writer, object instance)\n        {\n                \n        }\n\n        public void OnDeserializing(IFormatReader reader, Type instanceType)\n        {\n                \n        }\n\n        public void OnActivating(IFormatReader reader, Type instanceType)\n        {\n                \n        }\n\n        public void OnActivated(object instance)\n        {\n                \n        }\n\n        public void OnDeserialized(IFormatReader reader, object instance)\n        {\n                \n        }\n    }\n\n    public abstract class GenericInterceptor<T> : ISerializationInterceptor<T>\n    {\n        public virtual T Serializing(IFormatWriter writer, T instance) => instance;\n        public virtual T Deserialized(IFormatReader reader, T instance) => instance;\n        public abstract T Activating(Type instanceType);\n    }\n\n\n    /// <summary>\n    /// Important migration.  Label was changed to IAnnotationLabel, and existing serialized data\n    /// doesn't know to create Labels when it sees IAnnotationLabel (because \"exs:type\" attribute is omitted).\n    ///\n    /// If this is hit, it means we need to manually step in and specify the type of Label, or else it'll crash.\n    /// </summary>\n    public sealed class AnnotationLabelInterceptor : GenericInterceptor<IAnnotationLabel>\n    {\n        public static AnnotationLabelInterceptor Default { get; } = new();\n\n        // critical note:\n        // activate type of Label anytime we see IAnnotationLabel.\n        public override IAnnotationLabel Activating(Type instanceType) => new Label();\n    }\n    \n    public sealed class SnesDataInterceptor : GenericInterceptor<Data>\n    {\n        private readonly IDataFactory dataFactory;\n        public SnesDataInterceptor(IDataFactory dataFactory)\n        {\n            this.dataFactory = dataFactory;\n        }\n\n        // TODO: eventually make this IData not Data\n        public override Data Activating(Type instanceType) =>\n            dataFactory.Create();\n    }\n}"
  },
  {
    "path": "Diz.Core/services/CoreServices.cs",
    "content": "﻿using System;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.project;\nusing Diz.Core.model.snes;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.Core.services;\n\npublic static class DizCoreServicesDllRegistration\n{\n    public static void RegisterServicesInDizDlls(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.RegisterAssembly(\"Diz*.dll\");\n    }\n}\n\n[UsedImplicitly]\npublic class DizCoreServicesCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.Register<IFilesystemService, FilesystemService>();\n\n        serviceRegistry.Register<IData, Data>();\n\n        serviceRegistry.Register<IMigrationRunner, MigrationRunner>();\n\n        serviceRegistry.Register<IProjectFactory, ProjectFactory>();\n        serviceRegistry.Register<IProjectImportSettingsFactory, ProjectImportSettingsFactory>();\n        serviceRegistry.Register<IProjectImporter, ProjectImporter>();\n\n        serviceRegistry.Register<IProjectFileUserPrefs, ProjectFileUserPrefs>();\n        serviceRegistry.Register<IProjectFileManager, ProjectFileManager>();\n\n        serviceRegistry.Register<IXmlSerializerFactory, XmlSerializerFactory>();\n        \n        serviceRegistry.Register<IProjectXmlSerializer>(factory => new ProjectXmlSerializer(\n            xmlSerializerFactory: factory.GetInstance<IXmlSerializerFactory>(),\n            migrationRunner: factory.GetInstance<IMigrationRunner>()\n        ));\n\n        serviceRegistry.Register<IDataFactory, DataFactory>();\n\n        // default one that reads 1:1 from a file\n        serviceRegistry.Register<IFileByteProvider, FileByteProviderSingleFile>();\n        \n        serviceRegistry.Register<Func<string, IFileByteProvider>>(c => type =>\n        {\n            return type switch\n            {\n                \"Single\" => c.GetInstance<FileByteProviderSingleFile>(),\n                \"Multiple\" => c.GetInstance<FileByteProviderMultipleFiles>(),\n                _ => throw new InvalidOperationException($\"No file bytes type handler found for type: {type}\")\n            };\n        });\n        \n        serviceRegistry.Register<FileByteProviderSingleFile>();\n        serviceRegistry.Register<FileByteProviderMultipleFiles>();\n\n        serviceRegistry.Register<IDataFactory, XmlSerializerFactory.SnesDataInterceptor>((factory, dataFactory) => \n            new XmlSerializerFactory.SnesDataInterceptor(dataFactory));\n\n        serviceRegistry.RegisterFallback((type, serviceType) => \n            type == typeof(IReadFromFileBytes), \n            request => new ReadFromFileBytes());\n        \n        serviceRegistry.Register<ILinkedRomBytesProvider, LinkedRomBytesFileSearchProvider>();\n    }\n}"
  },
  {
    "path": "Diz.Core/util/ByteUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace Diz.Core.util;\n\npublic static class ByteUtil\n{\n    // take input addresses that can be in any formats that look like this, and convert to an int.\n    // This is useful if pasting addresses from other editors/tools/asm/etc trying to get a clean address.\n    // C0FFFF\n    // $C0FFFF\n    // C7/AAAA\n    // $C6/BBBB\n    // CODE_808066\n    // UNREACH_808066 and others\n    public static bool TryParseNum_Stripped(ref string addressTxt, NumberStyles style, out int address)\n    {\n        address = -1;\n\n        if (string.IsNullOrEmpty(addressTxt))\n            return false;\n            \n        // first, strip out some cases like \"CODE_xxxxxx\" or \"UNREACH_xxxxxx\"\n        const string regexPattern = @\".*_([A-Fa-f0-9]{6})\";\n        var match = Regex.Match(addressTxt, regexPattern);\n        if (match.Success)\n        {\n            // Extract the matched six-digit part\n            addressTxt = match.Groups[1].Value;\n        }\n\n        // secondly, strip some extra stuff out\n        var inputText = new string(Array.FindAll(addressTxt.ToCharArray(), (c =>\n                (char.IsLetterOrDigit(c) || char.IsWhiteSpace(c))\n            )));\n\n        if (!int.TryParse(inputText, style, null, out address))\n            return false;\n\n        addressTxt = inputText;\n        return true;\n    }\n\n    public delegate int AddressConverter(int address);\n\n    public static int ReadStringsTable(byte[] bytes, int startingIndex, int stringsPerEntry, AddressConverter converter, Action<int, string[]> processTableEntry)\n    {\n        var strings = new List<string>();\n\n        var pos = startingIndex;\n        var numTableEntries = ConvertByteArrayToInt32(bytes, pos);\n        pos += 4;\n\n        for (var entry = 0; entry < numTableEntries; ++entry)\n        {\n            var offset = converter(ConvertByteArrayToInt32(bytes, pos));\n            pos += 4;\n\n            strings.Clear();\n            for (var j = 0; j < stringsPerEntry; ++j)\n            {\n                pos += ReadNullTerminatedString(bytes, pos, out var str);\n                strings.Add(str);\n            }\n            processTableEntry(offset, strings.ToArray());\n        }\n\n        return pos - startingIndex;\n    }\n\n    public static int ReadNullTerminatedString(byte[] bytes, int startingOffset, out string str)\n    {\n        str = \"\";\n        var pos = startingOffset;\n        while (bytes[pos] != 0)\n            str += (char)bytes[pos++];\n        pos++;\n        return pos - startingOffset;\n    }\n\n    public static byte[] IntegerToByteArray(uint val)\n    {\n        return new[]\n        {\n            (byte)val,\n            (byte)(val >> 8),\n            (byte)(val >> 16),\n            (byte)(val >> 24)\n        };\n    }\n\n    public static void IntegerIntoByteArray(uint src, IList<byte> dst, int dstStartingOffset)\n    {\n        var arr = IntegerToByteArray(src);\n        for (var i = 0; i < arr.Length; i++) \n            dst[dstStartingOffset + i] = arr[i];\n    }\n\n    public static void AppendIntegerToByteList(uint src, IList<byte> dst)\n    {\n        var arr = IntegerToByteArray(src);\n        foreach (var t in arr)\n            dst.Add(t);\n    }\n\n    public static uint ConvertByteArrayToUInt32(IReadOnlyList<byte> src, int srcStartingOffset = 0)\n    {\n        return\n            src[srcStartingOffset] |\n            ((uint)src[srcStartingOffset + 1] << 8) |\n            ((uint)src[srcStartingOffset + 2] << 16) |\n            ((uint)src[srcStartingOffset + 3] << 24);\n    }\n\n    public static int ConvertByteArrayToInt32(IReadOnlyList<byte> src, int srcStartingOffset = 0)\n    {\n        return\n            src[srcStartingOffset] |\n            (src[srcStartingOffset + 1] << 8) |\n            (src[srcStartingOffset + 2] << 16) |\n            (src[srcStartingOffset + 3] << 24);\n    }\n\n    public static int ByteArrayToInt24(IReadOnlyList<byte> src, int srcStartingOffset = 0)\n    {\n        return\n            src[srcStartingOffset] |\n            (src[srcStartingOffset + 1] << 8) |\n            (src[srcStartingOffset + 2] << 16);\n    }\n\n    public static int ByteArrayToInt16(IReadOnlyList<byte> src, int srcStartingOffset = 0)\n    {\n        return\n            src[srcStartingOffset] |\n            (src[srcStartingOffset + 1] << 8);\n    }\n\n    public static byte[] StringToNullTermByteArray(string src)\n    {\n        var array = new byte[src.Length + 1];\n        for (var i = 0; i < src.Length; i++) \n            array[i] = (byte)src[i];\n            \n        array[src.Length] = 0;\n        return array;\n    }\n        \n        \n    // there's builtin C# code that parses Hex digits from a string, BUT, it's super-slow:\n    // slow --> Byte.Parse(x, isHex) \n    // \n    // this is less flexible but way faster, crucial for fast sections of our code. \n    // idea credit: Daniel-Lemire\n    private static readonly int[] HexAsciiToDigit = {\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,  7,  8,\n        9,  -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        -1, -1, -1, -1, -1, -1, -1, -1, -1};\n\n    // 4bit value\n    public static byte ByteParseHex1(char hexChar)\n    {\n        var result = HexAsciiToDigit[hexChar];\n        if (result == -1)\n            throw new InvalidDataException(\"Invalid hex digit\");\n\n        return (byte)result;\n    }\n\n    // 8bit value\n    public static byte ByteParseHex2(char hexChar1, char hexChar2)\n    {\n        return (byte)(ByteParseHex1(hexChar1) * 0x10 + ByteParseHex1(hexChar2));\n    }\n\n    // 16bit value\n    public static uint ByteParseHex4(char hexChar1, char hexChar2, char hexChar3, char hexChar4)\n    {\n        return\n            ByteParseHex1(hexChar1) * 0x1000u +\n            ByteParseHex1(hexChar2) * 0x100u +\n            ByteParseHex1(hexChar3) * 0x10u +\n            ByteParseHex1(hexChar4);\n    }\n\n    public static uint ByteParseHex(string str)\n    {\n        return ByteParseHex(str, 0, str.Length);\n    }\n\n    // note: this helper isn't quite as fast, use one of the other ByteParseHex1/2/3/4() functions directly\n    // if you need the best performance.\n    //\n    // this function is a faster but more specific implementation of:\n    // Convert.ToInt32(line.Substring(startIndex, length), 16);\n    public static uint ByteParseHex(string str, int strStartIndex, int numHexDigits)\n    {\n        if (numHexDigits is <= 0 or > 8)\n            throw new ArgumentException(\"numHexDigits out of range\");\n\n        var offset = numHexDigits - 1;\n        var multiplier = 1u;\n        var result = 0u;\n            \n        for (var i = 0; i < numHexDigits; ++i)\n        {\n            if (numHexDigits >= i + 1)\n            {\n                result += ByteParseHex1(str[strStartIndex + offset]) * multiplier;\n                offset--;\n            }\n            multiplier *= 0x10;\n        }\n\n        return result;\n    }\n        \n    // Cart names in the ROM use \"shift-JIS\" encoding, which is ASCII with extra japanese chars.\n    // SNES games use it for their text fields in the header, particularly the cartridge title.\n    // This needs to be parsed carefully.\n    private static bool _registeredEncoding;\n    public static Encoding ShiftJisEncoding\n    {\n        get\n        {\n            if (!_registeredEncoding)\n            {\n                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);\n                _registeredEncoding = true;\n            }\n\n            return Encoding.GetEncoding(932);\n        }\n    }\n\n    public static string ReadShiftJisEncodedString(IReadOnlyList<byte> buffer, int index = 0, int count = -1) => \n        ReadStringFromByteArray(buffer, index, count, ShiftJisEncoding);\n        \n    public static byte[] ConvertUtf8ToShiftJisEncodedBytes(string str) => \n        Encoding.Convert(Encoding.UTF8, ShiftJisEncoding, Encoding.UTF8.GetBytes(str));\n        \n    public static byte[] GetRawShiftJisBytesFromStr(string inputStr)\n    {\n        var shiftJisEncodedBytes = ConvertUtf8ToShiftJisEncodedBytes(inputStr);\n        var shiftJisStr = ShiftJisEncoding.GetString(shiftJisEncodedBytes);\n        var rawShiftJisBytes = ShiftJisEncoding.GetBytes(shiftJisStr);\n        return rawShiftJisBytes;\n    }\n        \n    // pad any empty space (for a total of 21 bytes) using the space (0x20) character\n    public static byte[] PadCartridgeTitleBytes(byte[] srcBytes) => \n        PadBytes(srcBytes, RomUtil.LengthOfTitleName - srcBytes.Length);\n\n    private static byte[] PadBytes(IEnumerable<byte> srcBytes, int numPadBytesNeeded) => \n        srcBytes.Concat(Enumerable.Repeat((byte) 0x20, numPadBytesNeeded)).ToArray();\n\n    // read a fixed length string from an array of bytes. does not check for null termination.\n    // allows using a non-UTF8 encoding\n    public static string ReadStringFromByteArray(IReadOnlyList<byte> bytes, int index = 0, int count = -1, Encoding srcEncoding = null)\n    {\n        var utfBytes = Encoding.Convert(\n            srcEncoding ?? Encoding.UTF8, \n            Encoding.UTF8, \n            bytes.ToArray(), \n            index, \n            count != -1 ? count : bytes.Count);\n            \n        return Encoding.UTF8.GetString(utfBytes);\n    }\n}"
  },
  {
    "path": "Diz.Core/util/ChecksumUtil.cs",
    "content": "﻿// ReSharper disable InconsistentNaming\n// ReSharper disable IdentifierTypo\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Diz.Core.Interfaces;\n\nnamespace Diz.Core.util\n{\n    public static class ChecksumUtil\n    {\n        #region Diz interface\n        \n        public static uint ComputeChecksumFromRom(IList<byte> romdata) => \n            AsarChecksumUtil.getchecksum(romdata);\n        public static bool IsRomChecksumValid(IList<byte> romdata, RomMapMode mode, int size) => \n            AsarChecksumUtil.goodchecksum(romdata, mode, size);\n        public static void UpdateRomChecksum(IList<byte> romdata, RomMapMode mode, int size) => \n            AsarChecksumUtil.fixchecksum(romdata, mode, size);\n        \n        #endregion\n\n        // try to not modify the code here too much to keep in sync with Asar upstream C++ code,\n        // in case they ever have bugfixes/etc.\n        // adapted from https://github.com/RPGHacker/asar/blob/master/src/asar/libsmw.cpp\n        // original authors: randomdude9999, RPGHacker, CypherSignal, p4plus2, probably others\n        private static class AsarChecksumUtil\n        {\n            #region Checksum implementation, adapted from Asar\n\n            [SuppressMessage(\"ReSharper\", \"SuggestVarOrType_BuiltInTypes\")]\n            internal static uint getchecksum(IList<byte> romdata)\n            {\n                uint romlen = (uint) romdata.Count;\n                uint checksum = 0;\n                if ((romlen & (romlen - 1)) == 0)\n                {\n                    // romlen is a power of 2, just add up all the bytes\n                    for (var i = 0; i < romlen; i++)\n                        checksum += romdata[i];\n                }\n                else\n                {\n                    // assume romlen is the sum of 2 powers of 2 - i haven't seen any real rom that isn't,\n                    // and if you make such a rom, fixing its checksum is your problem.\n                    uint firstpart = bitround(romlen) >> 1;\n                    uint secondpart = romlen - firstpart;\n                    uint repeatcount = firstpart / secondpart;\n                    uint secondpart_sum = 0;\n                    for (int i = 0; i < firstpart; i++) checksum += romdata[i];\n                    for (int i = (int) firstpart; i < romlen; i++) secondpart_sum += romdata[i];\n                    checksum += secondpart_sum * repeatcount;\n                }\n\n                return checksum & 0xFFFF;\n            }\n\n            internal static bool goodchecksum(IList<byte> romdata, RomMapMode mode, int size)\n            {\n                int snestopc(int snesAddress) => RomUtil.ConvertSnesToPc(snesAddress, mode, size);\n\n                var checksum = (int) getchecksum(romdata);\n                return ((romdata[snestopc(0x00FFDE)] ^ romdata[snestopc(0x00FFDC)]) == 0xFF) &&\n                       ((romdata[snestopc(0x00FFDF)] ^ romdata[snestopc(0x00FFDD)]) == 0xFF) &&\n                       ((romdata[snestopc(0x00FFDE)] & 0xFF) == (checksum & 0xFF)) &&\n                       ((romdata[snestopc(0x00FFDF)] & 0xFF) == ((checksum >> 8) & 0xFF));\n            }\n\n            internal static void fixchecksum(IList<byte> romdata, RomMapMode mode, int size)\n            {\n                int snestopc(int snesAddress) => RomUtil.ConvertSnesToPc(snesAddress, mode, size);\n\n                // randomdude999: clear out checksum bytes before recalculating checksum, this should make it correct on roms that don't have a checksum yet\n                romdata.writeromdata(snestopc(0x00FFDC), 0xFFFF0000);\n                var checksum = getchecksum(romdata);\n                romdata.writeromdata_byte(snestopc(0x00FFDE), (byte) (checksum & 255));\n                romdata.writeromdata_byte(snestopc(0x00FFDF), (byte) ((checksum >> 8) & 255));\n                romdata.writeromdata_byte(snestopc(0x00FFDC), (byte) ((checksum & 255) ^ 255));\n                romdata.writeromdata_byte(snestopc(0x00FFDD), (byte) (((checksum >> 8) & 255) ^ 255));\n            }\n\n            private static uint bitround(uint x)\n            {\n                if ((x & (x - 1)) == 0) return x;\n                while ((x & (x - 1)) != 0) x &= x - 1;\n                return x << 1;\n            }\n\n            #endregion\n        }\n        \n        #region Diz implementation of Asar functions\n        private static void writeromdata_byte(this IList<byte> @this, int pcOffset, byte val) =>\n            @this[pcOffset] = val;\n\n        private static void writeromdata(this IList<byte> @this, int pcOffset, uint val32) =>\n            ByteUtil.IntegerIntoByteArray(val32, @this, pcOffset);\n        #endregion\n    }\n}"
  },
  {
    "path": "Diz.Core/util/CorrectingRange.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\n\nnamespace Diz.Core.util\n{\n    // when any parameter is set, the others will adjust to be in range (if possible)\n    // i.e. if you set a starting index, the count will adjust itself to make sure the invariant holds\n    public class CorrectingRange : IDataRange\n    {\n        public int MaxCount { get; }\n\n        private int rangeStartIndex;\n        private int rangeCount;\n\n        public CorrectingRange(int maxCount)\n        {\n            MaxCount = maxCount;\n        }\n\n        public int EndIndex\n        {\n            // EndIndex is just a convenience helper, we'll route everything through StartIndex\n            get => RangeCount == 0 \n                ? -1 \n                : StartIndex + RangeCount - 1;\n            set => StartIndex = value - RangeCount + 1;\n        }\n\n        public int StartIndex\n        {\n            get => rangeStartIndex;\n            set\n            {\n                rangeStartIndex = ClampIndex(value);\n                OnStartIndexChanged();\n                AssertValid();\n            }\n        }\n        \n        public int RangeCount\n        {\n            get => rangeCount;\n            set\n            {\n                rangeCount = ClampCount(value);\n                OnRangeCountChanged();\n                AssertValid();\n            }\n        }\n\n        public void ManualUpdate(int newStartIndex, int newRangeCount)\n        {\n            rangeStartIndex = ClampIndex(newStartIndex);\n            rangeCount = ClampCount(newRangeCount);\n            AssertValid();\n        }\n\n        private void OnStartIndexChanged() => UpdateRangeCountToBounds();\n        private void OnRangeCountChanged() => UpdateStartIndexToBounds();\n\n        private void UpdateRangeCountToBounds() => rangeCount = ClampCount(rangeCount);\n        private void UpdateStartIndexToBounds() => rangeStartIndex = ClampIndex(rangeStartIndex);\n\n        private int ClampCount(int count)\n        { \n            if (count <= 0)\n                return 0;\n\n            if (!IsValidIndex(rangeStartIndex + count - 1)) \n                count = MaxCount - rangeStartIndex;\n\n            Debug.Assert(Util.IsBetween(count, MaxCount));\n            return count;\n        }\n\n        private bool IsValidIndex(int index) =>\n            index >= 0 && index < MaxCount;\n        \n        private int ClampIndex(int index)\n        {\n            var clampedIndex = Util.ClampIndex(index, MaxCount);\n            if (!IsValidIndex(clampedIndex))\n                throw new ArgumentOutOfRangeException(nameof(index));\n            \n            return clampedIndex;\n        }\n        \n        [Conditional(\"DEBUG\")]\n        private void AssertValid()\n        {\n            Debug.Assert(MaxCount >= 0);\n            \n            Debug.Assert(rangeCount >= 0);\n            Debug.Assert(rangeStartIndex >= 0);\n\n            if (rangeCount == 0)\n            {\n                Debug.Assert(EndIndex == -1);\n            }\n            else\n            {\n                Debug.Assert(EndIndex >= 0);\n            }\n\n            Debug.Assert(rangeCount <= MaxCount);\n            Debug.Assert(rangeStartIndex < MaxCount);\n            Debug.Assert(EndIndex < MaxCount);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/util/Fake64Encoding.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Diz.Core.util\n{\n    // this is base64 EXCEPT:\n    // 1) char \"A\" and \"0\" swapped (my data format compresses things with \"0\" better)\n    // 2) only supports 6 bits of input data to encode on ONE BYTE.\n    //\n    // there's better ways to do this but I'm going for blinding speed for multi-threaded access.\n    // just, hardcode it.  there are system Base64 functions we could use but, they're also way too slow.\n    //\n    // This is ... kind of a silly implementation... but, it's fast and works well with our compression so...\n    // if you don't like it that's just like... your opinion and stuff. man.♦\n    //\n    // [don't judge me] -Dom\n    public static class Fake64Encoding\n    {\n        public static byte DecodeHackyBase64(char input)\n        {\n            return Fake64CharToByte[input];\n        }\n\n        public static char EncodeHackyBase64(byte input)\n        {\n            return Fake64ByteToChar[input];\n        }\n        \n        // char to byte\n        private static readonly Dictionary<char, byte> Fake64CharToByte = new()\n        {\n            {'A', 208},\n            {'B', 4},\n            {'C', 8},\n            {'D', 12},\n            {'E', 16},\n            {'F', 20},\n            {'G', 24},\n            {'H', 28},\n            {'I', 32},\n            {'J', 36},\n            {'K', 40},\n            {'L', 44},\n            {'M', 48},\n            {'N', 52},\n            {'O', 56},\n            {'P', 60},\n            {'Q', 64},\n            {'R', 68},\n            {'S', 72},\n            {'T', 76},\n            {'U', 80},\n            {'V', 84},\n            {'W', 88},\n            {'X', 92},\n            {'Y', 96},\n            {'Z', 100},\n            {'a', 104},\n            {'b', 108},\n            {'c', 112},\n            {'d', 116},\n            {'e', 120},\n            {'f', 124},\n            {'g', 128},\n            {'h', 132},\n            {'i', 136},\n            {'j', 140},\n            {'k', 144},\n            {'l', 148},\n            {'m', 152},\n            {'n', 156},\n            {'o', 160},\n            {'p', 164},\n            {'q', 168},\n            {'r', 172},\n            {'s', 176},\n            {'t', 180},\n            {'u', 184},\n            {'v', 188},\n            {'w', 192},\n            {'x', 196},\n            {'y', 200},\n            {'z', 204},\n            {'0', 0},\n            {'1', 212},\n            {'2', 216},\n            {'3', 220},\n            {'4', 224},\n            {'5', 228},\n            {'6', 232},\n            {'7', 236},\n            {'8', 240},\n            {'9', 244},\n            {'+', 248},\n            {'/', 252},\n        };\n\n        private static readonly Dictionary<byte, char> Fake64ByteToChar = new()\n        {\n            {208, 'A'},\n            {4, 'B'},\n            {8, 'C'},\n            {12, 'D'},\n            {16, 'E'},\n            {20, 'F'},\n            {24, 'G'},\n            {28, 'H'},\n            {32, 'I'},\n            {36, 'J'},\n            {40, 'K'},\n            {44, 'L'},\n            {48, 'M'},\n            {52, 'N'},\n            {56, 'O'},\n            {60, 'P'},\n            {64, 'Q'},\n            {68, 'R'},\n            {72, 'S'},\n            {76, 'T'},\n            {80, 'U'},\n            {84, 'V'},\n            {88, 'W'},\n            {92, 'X'},\n            {96, 'Y'},\n            {100, 'Z'},\n            {104, 'a'},\n            {108, 'b'},\n            {112, 'c'},\n            {116, 'd'},\n            {120, 'e'},\n            {124, 'f'},\n            {128, 'g'},\n            {132, 'h'},\n            {136, 'i'},\n            {140, 'j'},\n            {144, 'k'},\n            {148, 'l'},\n            {152, 'm'},\n            {156, 'n'},\n            {160, 'o'},\n            {164, 'p'},\n            {168, 'q'},\n            {172, 'r'},\n            {176, 's'},\n            {180, 't'},\n            {184, 'u'},\n            {188, 'v'},\n            {192, 'w'},\n            {196, 'x'},\n            {200, 'y'},\n            {204, 'z'},\n            {0, '0'},\n            {212, '1'},\n            {216, '2'},\n            {220, '3'},\n            {224, '4'},\n            {228, '5'},\n            {232, '6'},\n            {236, '7'},\n            {240, '8'},\n            {244, '9'},\n            {248, '+'},\n            {252, '/'},\n        };\n    }\n}\n"
  },
  {
    "path": "Diz.Core/util/FsUtils.cs",
    "content": "﻿#nullable enable\n\nusing System.IO;\n\nnamespace Diz.Core.util;\n\npublic interface IFilesystemService\n{\n    bool DirectoryExists(string? outputDirectoryName);\n    void CreateDirectory(string name);\n}\n\n/// <summary>\n/// Wrapper for OS-level path/file manipulation.\n/// Mostly for ease of unit testing\n/// </summary>\npublic class FilesystemService : IFilesystemService\n{\n    public virtual bool DirectoryExists(string? outputDirectoryName) => \n        Directory.Exists(outputDirectoryName);\n\n    public virtual void CreateDirectory(string name) => \n        Directory.CreateDirectory(name);\n}"
  },
  {
    "path": "Diz.Core/util/LabelSearchTerms.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing Diz.Core.Interfaces;\n\nnamespace Diz.Core.util;\n\npublic class LabelSearchTerms\n{\n    private readonly string[] mustMatchAllStrings = [];\n    private bool filterToRamOnly;\n    private readonly List<AddressComparison> addressFilters = [];\n    private class AddressComparison\n    {\n        public enum ComparisonType\n        {\n            GreaterThan,\n            GreaterThanOrEqual,\n            LessThan,\n            LessThanOrEqual\n        }\n\n        public ComparisonType Type { get; init; }\n        public int Address { get; init; }\n    }\n\n    public LabelSearchTerms(string searchInput)\n    {\n        if (string.IsNullOrWhiteSpace(searchInput))\n            return;\n\n        var terms = searchInput.Split(' ', StringSplitOptions.RemoveEmptyEntries);\n        var stringTerms = new List<string>();\n\n        foreach (var term in terms)\n        {\n            if (TryParseSpecialTerm(term))\n                continue;\n\n            // If it's not a special term, add it to the string search terms\n            stringTerms.Add(term);\n        }\n\n        mustMatchAllStrings = stringTerms.ToArray();\n    }\n\n    private bool TryParseSpecialTerm(string term)\n    {\n        // Handle \"is:ram\" filter\n        if (term.Equals(\"is:ram\", StringComparison.OrdinalIgnoreCase))\n        {\n            filterToRamOnly = true;\n            return true;\n        }\n\n        // Handle address comparisons (>, >=, <, <=)\n        var addressComparison = TryParseAddressComparison(term);\n        if (addressComparison != null)\n        {\n            addressFilters.Add(addressComparison);\n            return true;\n        }\n\n        // nothing special about it\n        return false;\n    }\n\n    private AddressComparison? TryParseAddressComparison(string term)\n    {\n        if (term.Length < 2)\n            return null;\n\n        AddressComparison.ComparisonType comparisonType;\n        string addressPart;\n\n        // Check for two-character operators first\n        if (term.StartsWith(\">=\"))\n        {\n            comparisonType = AddressComparison.ComparisonType.GreaterThanOrEqual;\n            addressPart = term.Substring(2);\n        }\n        else if (term.StartsWith(\"<=\"))\n        {\n            comparisonType = AddressComparison.ComparisonType.LessThanOrEqual;\n            addressPart = term.Substring(2);\n        }\n        // Then check single-character operators\n        else if (term.StartsWith(\">\"))\n        {\n            comparisonType = AddressComparison.ComparisonType.GreaterThan;\n            addressPart = term.Substring(1);\n        }\n        else if (term.StartsWith(\"<\"))\n        {\n            comparisonType = AddressComparison.ComparisonType.LessThan;\n            addressPart = term.Substring(1);\n        }\n        else\n        {\n            return null;\n        }\n\n        // Try to parse the address part\n        var parsedAddress = TryParseHexAddress(addressPart) ?? -1;\n        if (parsedAddress == -1)\n            return null;\n        \n        return new AddressComparison\n        {\n            Type = comparisonType,\n            Address = parsedAddress\n        };\n    }\n\n    private static int? TryParseHexAddress(string addressString)\n    {\n        if (string.IsNullOrEmpty(addressString))\n            return null;\n\n        // Remove $ prefix if present\n        if (addressString.StartsWith(\"$\"))\n            addressString = addressString.Substring(1);\n\n        // Try to parse as hexadecimal\n        if (int.TryParse(addressString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var address))\n            return address;\n\n        return null;\n    }\n    \n    public bool DoesLabelMatch(int snesAddress, IAnnotationLabel label)\n    {\n        // check that our label satisfies all of various conditions to count as a match\n        \n        if (filterToRamOnly)\n        {\n            // condition: we are required to be a RAM address or, no match\n            if (RomUtil.GetWramAddressFromSnesAddress(snesAddress) == -1)\n                return false;\n        }\n\n        // condition: address in range specified\n        if (addressFilters.Select(filter => filter.Type switch\n            {\n                AddressComparison.ComparisonType.GreaterThan => snesAddress > filter.Address,\n                AddressComparison.ComparisonType.GreaterThanOrEqual => snesAddress >= filter.Address,\n                AddressComparison.ComparisonType.LessThan => snesAddress < filter.Address,\n                AddressComparison.ComparisonType.LessThanOrEqual => snesAddress <= filter.Address,\n                _ => true\n            }).Any(addressMatches => !addressMatches))\n        {\n            return false;\n        }\n\n        // condition: we must match all required strings\n        // ReSharper disable once InvertIf\n        if (mustMatchAllStrings.Length > 0)\n        {\n            var allContextText = label.ContextMappings\n                .Select(x => x.NameOverride)\n                .Aggregate(\"\", (current, contextName) => current + $\"{contextName} \");\n\n            // build a string with all searchable text from all fields in the label:\n            var allText = $\"{Util.ToHexString6(snesAddress)} {label.Name} {label.Comment} {allContextText}\";\n            \n            // search through that string, we have to find something in it for all search terms\n            return mustMatchAllStrings\n                .All(term => allText.Contains(term, StringComparison.CurrentCultureIgnoreCase));\n        }\n\n        return true;\n    }\n}"
  },
  {
    "path": "Diz.Core/util/ParentAwareCollection.cs",
    "content": "﻿using System.Collections.ObjectModel;\nusing System.Xml.Serialization;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.util\n{\n    public interface IParentAware<in TParent>\n    {\n        public void OnParentChanged(TParent parent);\n    }\n\n    public class ParentAwareCollection<TParent, TItem> : Collection<TItem>\n        where TParent : class\n        where TItem : IParentAware<TParent>\n    {\n        private TParent parent;\n        [XmlIgnore] \n        public bool DontSetParentOnCollectionItems { get; set; }\n\n        [PublicAPI]\n        [XmlIgnore] \n        public TParent Parent\n        {\n            get => parent;\n            set\n            {\n                parent = value;\n                SetAllItemParentsTo(parent);\n            }\n        }\n\n        private void SetItemParent(TItem item, TParent newParent)\n        {\n            if (DontSetParentOnCollectionItems)\n                return;\n            \n            item?.OnParentChanged(newParent);\n        }\n\n        private void SetAllItemParentsTo(TParent newParent)\n        {\n            foreach (var item in this)\n            {\n                SetItemParent(item, newParent);\n            }\n        }\n\n        protected override void ClearItems()\n        {\n            SetAllItemParentsTo(null);\n            base.ClearItems();\n        }\n\n        protected override void InsertItem(int index, TItem item)\n        {\n            base.InsertItem(index, item);\n            SetItemParent(item, Parent);\n        }\n\n        protected override void RemoveItem(int index)\n        {\n            var item = Items[index];\n            SetItemParent(item, null);\n            base.RemoveItem(index);\n        }\n\n        protected override void SetItem(int index, TItem item)\n        {\n            base.SetItem(index, item);\n            SetItemParent(item, Parent);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/util/Profiler.cs",
    "content": "﻿#define JETBRAINS_PROFILING_ENABLED\n\nusing System;\nusing JetBrains.Profiler.SelfApi;\n#if JETBRAINS_PROFILING_ENABLED\n\n#endif\n\nnamespace Diz.Core.util\n{\n    // setup:\n    // ProfilerDotTrace.Enabled = true; // can take a bit to download packages when run for first time\n    //\n    // to capture a snapshot of one function or scope:\n    // using var captureSession = new CaptureSnapshot();\n    //\n    // or, to manually start/stop\n    // ProfilerDotTrace.BeginSnapshot() / ProfilerDotTrace.EndSnapshot() \n    \n    public static class ProfilerDotTrace\n    {\n        public class CaptureSnapshot : IDisposable\n        {\n            private readonly bool skipped;\n            public CaptureSnapshot(bool shouldSkip = false)\n            {\n                skipped = shouldSkip;\n                if (!shouldSkip)\n                    BeginSnapshot();\n            }\n\n            private void ReleaseUnmanagedResources()\n            {\n                if (!skipped)\n                    EndSnapshot();\n            }\n\n            public void Dispose()\n            {\n                ReleaseUnmanagedResources();\n                GC.SuppressFinalize(this);\n            }\n\n            ~CaptureSnapshot()\n            {\n                ReleaseUnmanagedResources();\n            }\n        }\n\n        public static bool Enabled\n        {\n            get => _enabled;\n            set\n            {\n                if (!value && _enabled)\n                {\n                    Shutdown();\n                }\n\n                _enabled = value;\n\n                if (_enabled)\n                {\n                    // initialize the API and download the tool (if needed)\n                    DotTrace.EnsurePrerequisite();\n                    Init();\n                }\n            }\n        }\n\n        private static bool _initialized;\n        private static bool _enabled;\n        private static bool _capturingSnapshot;\n\n        // returns true if we're initialized\n        // false if we weren't able to initialize\n        private static bool Init()\n        {\n            if (!Enabled)\n                return false;\n\n            if (_initialized)\n                return true;\n\n#if JETBRAINS_PROFILING_ENABLED\n            var config = new DotTrace.Config();\n            config.SaveToDir(\"c:\\\\tmp\\\\snapshot\");\n            DotTrace.Attach(config);\n\n            // now ready, call DotTrace.StartCollectingData() to begin snapshot.\n#endif\n\n            return _initialized = true;\n        }\n\n        public static void BeginSnapshot()\n        {\n            if (!Init() || _capturingSnapshot)\n                return;\n\n#if JETBRAINS_PROFILING_ENABLED\n            DotTrace.StartCollectingData();\n#endif\n\n            _capturingSnapshot = true;\n        }\n\n        public static void EndSnapshot()\n        {\n            if (!_initialized || !_capturingSnapshot)\n                return;\n\n            // stop collecting current snaphot and save it to disk.\n            // after this, need to call StartCollectingData() again to start a new snapshot, or quit.\n            DotTrace.SaveData();\n\n            _capturingSnapshot = false;\n        }\n\n        public static void Shutdown()\n        {\n            if (!_initialized)\n                return;\n\n            EndSnapshot();\n\n#if JETBRAINS_PROFILING_ENABLED\n            DotTrace.Detach();\n#endif\n\n            _initialized = false;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/util/RomUtil.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.IO;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.util\n{\n    public static class RomUtil\n    {\n        public const int LoromSettingOffset = 0x7FD5;\n        public const int HiromSettingOffset = 0xFFD5;\n        public const int ExhiromSettingOffset = 0x40FFD5;\n        public const int ExloromSettingOffset = 0x407FD5;\n        \n        public static int CalculateSnesOffsetWithWrap(int snesAddress, int offset)\n        {\n            return (GetBankFromSnesAddress(snesAddress) << 16) + ((snesAddress + offset) & 0xFFFF);\n        }\n\n        public static int GetBankFromSnesAddress(int snesAddress)\n        {\n            return (snesAddress >> 16) & 0xFF;\n        }\n\n        public static int GetBankSize(RomMapMode mode)\n        {\n            // todo\n            return mode == RomMapMode.LoRom ? 0x8000 : 0x10000;\n        }\n\n        public static RomSpeed GetRomSpeed(RomMapMode mode, IReadOnlyList<byte> romBytes) =>\n            GetRomSpeed(GetRomSettingOffset(mode), romBytes);\n\n        public static RomSpeed GetRomSpeed(int offset, IReadOnlyList<byte> romBytes) =>\n            offset < romBytes.Count\n                ? (romBytes[offset] & 0x10) != 0 ? RomSpeed.FastRom : RomSpeed.SlowRom\n                : RomSpeed.Unknown;\n        \n        \n        /// <summary>\n        /// Return the \"title\" information (i.e. the name of the game) from the SNES ROM header\n        /// </summary>\n        /// <param name=\"allRomBytes\">All the bytes in a ROM</param>\n        /// <param name=\"romSettingOffset\">Offset of the start of the SNES header section (title info is before this)</param>\n        /// <returns>UTF8 string of the title, padded with spaces</returns>\n        public static string GetCartridgeTitleFromRom(IReadOnlyList<byte> allRomBytes, int romSettingOffset) => \n            GetCartridgeTitleFromBuffer(allRomBytes, GetCartridgeTitleStartingRomOffset(romSettingOffset));\n\n        // input: ROM setting offset (pcOffset, NOT snes address)\n        public static int GetCartridgeTitleStartingRomOffset(int romSettingOffset) => \n            romSettingOffset - LengthOfTitleName;\n\n        /// <summary>\n        /// Return the \"title\" information (i.e. the name of the game) from an arbitrary buffer\n        /// </summary>\n        /// <param name=\"buffer\">Array of bytes</param>\n        /// <param name=\"index\">Index into the array to start with</param>\n        /// <returns>UTF8 string of the title, padded with spaces</returns>\n        public static string GetCartridgeTitleFromBuffer(IReadOnlyList<byte> buffer, int index = 0) => \n            ByteUtil.ReadShiftJisEncodedString(buffer, index, LengthOfTitleName);\n\n        public static int ConvertSnesToPc(int address, RomMapMode mode, int size)\n        {\n            var index = ConvertSnesToPcRaw(address, mode, size);\n            return index < 0 ? -1 : index;\n        }\n\n        private static int ConvertSnesToPcRaw(int address, RomMapMode mode, int size)\n        {\n            int GetUnmirroredOffset(int offset) => UnmirroredOffset(offset, size);\n\n            // WRAM is N/A to PC addressing\n            if ((address & 0xFE0000) == 0x7E0000) return -1;\n\n            // WRAM mirror & PPU regs are N/A to PC addressing\n            if (((address & 0x400000) == 0) && ((address & 0x8000) == 0)) return -1;\n\n            switch (mode)\n            {\n                case RomMapMode.LoRom:\n                {\n                    // SRAM is N/A to PC addressing\n                    if (((address & 0x700000) == 0x700000) && ((address & 0x8000) == 0))\n                        return -1;\n\n                    return GetUnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF));\n                }\n                case RomMapMode.HiRom:\n                {\n                    return GetUnmirroredOffset(address & 0x3FFFFF);\n                }\n                case RomMapMode.SuperMmc:\n                {\n                    return GetUnmirroredOffset(address & 0x3FFFFF); // todo, treated as hirom atm\n                }\n                case RomMapMode.Sa1Rom:\n                case RomMapMode.ExSa1Rom:\n                {\n                    // BW-RAM is N/A to PC addressing\n                    if (address >= 0x400000 && address <= 0x7FFFFF) return -1;\n\n                    if (address >= 0xC00000)\n                        return mode == RomMapMode.ExSa1Rom\n                            ? GetUnmirroredOffset(address & 0x7FFFFF)\n                            : GetUnmirroredOffset(address & 0x3FFFFF);\n\n                    if (address >= 0x800000) address -= 0x400000;\n\n                    // SRAM is N/A to PC addressing\n                    if (((address & 0x8000) == 0)) return -1;\n\n                    return GetUnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF));\n                }\n                case RomMapMode.SuperFx:\n                {\n                    // BW-RAM is N/A to PC addressing\n                    if (address >= 0x600000 && address <= 0x7FFFFF)\n                        return -1;\n\n                    if (address < 0x400000)\n                        return GetUnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF));\n\n                    if (address < 0x600000)\n                        return GetUnmirroredOffset(address & 0x3FFFFF);\n\n                    if (address < 0xC00000)\n                        return 0x200000 + GetUnmirroredOffset(((address & 0x7F0000) >> 1) | (address & 0x7FFF));\n\n                    return 0x400000 + GetUnmirroredOffset(address & 0x3FFFFF);\n                }\n                case RomMapMode.ExHiRom:\n                {\n                    return GetUnmirroredOffset(((~address & 0x800000) >> 1) | (address & 0x3FFFFF));\n                }\n                case RomMapMode.ExLoRom:\n                {\n                    // SRAM is N/A to PC addressing\n                    if (((address & 0x700000) == 0x700000) && ((address & 0x8000) == 0))\n                        return -1;\n\n                    return GetUnmirroredOffset((((address ^ 0x800000) & 0xFF0000) >> 1) | (address & 0x7FFF));\n                }\n                default:\n                {\n                    return -1;\n                }\n            }\n        }\n\n        public static int ConvertPCtoSnes(int offset, RomMapMode romMapMode, RomSpeed romSpeed)\n        {\n            switch (romMapMode)\n            {\n                case RomMapMode.LoRom:\n                    offset = ((offset & 0x3F8000) << 1) | 0x8000 | (offset & 0x7FFF);\n                    if (romSpeed == RomSpeed.FastRom || offset >= 0x7E0000) offset |= 0x800000;\n                    return offset;\n                case RomMapMode.HiRom:\n                    offset |= 0x400000;\n                    if (romSpeed == RomSpeed.FastRom || offset >= 0x7E0000) offset |= 0x800000;\n                    return offset;\n                case RomMapMode.ExHiRom when offset < 0x40000:\n                    offset |= 0xC00000;\n                    return offset;\n                case RomMapMode.ExHiRom:\n                    if (offset >= 0x7E0000) offset &= 0x3FFFFF;\n                    return offset;\n                case RomMapMode.ExSa1Rom when offset >= 0x400000:\n                    offset += 0x800000;\n                    return offset;\n            }\n\n            offset = ((offset & 0x3F8000) << 1) | 0x8000 | (offset & 0x7FFF);\n            if (offset >= 0x400000) offset += 0x400000;\n\n            return offset;\n        }\n\n        public static int UnmirroredOffset(int offset, int size)\n        {\n            // most of the time this is true; for efficiency\n            if (offset < size) \n                return offset;\n\n            int repeatSize = 0x8000;\n            while (repeatSize < size) repeatSize <<= 1;\n\n            int repeatedOffset = offset % repeatSize;\n\n            // this will then be true for ROM sizes of powers of 2\n            if (repeatedOffset < size) return repeatedOffset;\n\n            // for ROM sizes not powers of 2, it's kinda ugly\n            int sizeOfSmallerSection = 0x8000;\n            while (size % (sizeOfSmallerSection << 1) == 0) sizeOfSmallerSection <<= 1;\n\n            while (repeatedOffset >= size) repeatedOffset -= sizeOfSmallerSection;\n            return repeatedOffset;\n        }\n\n        // TODO: these can be attributes on the enum itself. like [AsmLabel(\"UNREACH\")]\n        public static string TypeToLabel(FlagType flag)\n        {\n            return flag switch\n            {\n                FlagType.Unreached => \"UNREACH\",\n                FlagType.Opcode => \"CODE\",\n                FlagType.Operand => \"LOOSE_OP\",\n                FlagType.Data8Bit => \"DATA8\",\n                FlagType.Graphics => \"GFX\",\n                FlagType.Music => \"MUSIC\",\n                FlagType.Empty => \"EMPTY\",\n                FlagType.Data16Bit => \"DATA16\",\n                FlagType.Pointer16Bit => \"PTR16\",\n                FlagType.Data24Bit => \"DATA24\",\n                FlagType.Pointer24Bit => \"PTR24\",\n                FlagType.Data32Bit => \"DATA32\",\n                FlagType.Pointer32Bit => \"PTR32\",\n                FlagType.Text => \"TEXT\",\n                _ => \"\"\n            };\n        }\n\n        public static int GetByteLengthForFlag(FlagType flag)\n        {\n            switch (flag)\n            {\n                case FlagType.Unreached:\n                case FlagType.Opcode:\n                case FlagType.Operand:\n                case FlagType.Data8Bit:\n                case FlagType.Graphics:\n                case FlagType.Music:\n                case FlagType.Empty:\n                case FlagType.Text:\n                    return 1;\n                case FlagType.Data16Bit:\n                case FlagType.Pointer16Bit:\n                    return 2;\n                case FlagType.Data24Bit:\n                case FlagType.Pointer24Bit:\n                    return 3;\n                case FlagType.Data32Bit:\n                case FlagType.Pointer32Bit:\n                    return 4;\n            }\n            return 0;\n        }\n\n        public static RomMapMode DetectRomMapMode(IReadOnlyList<byte> romBytes, out bool detectedValidRomMapType)\n        {\n            // this is not bulletproof.\n            // some of these detections may be false positives if you have bad luck.\n            // for instance, if your rom is really HiRom, but, romBytes[LoromSettingOffset] & 0xEC) == 0x20, \n            // then it will incorrect detect as lorom.\n            //\n            // best to add some other scoring capabilities in here, like, check if the detected settings give you\n            // the offset of a cartridge title that's all ascii (instead of a bunch of garbage).\n            //\n            // maybe steal the detection code from other emulators and paste it in here.\n            \n            detectedValidRomMapType = true;\n\n            if ((romBytes[LoromSettingOffset] & 0xEF) == 0x23)\n                return romBytes.Count > 0x400000 ? RomMapMode.ExSa1Rom : RomMapMode.Sa1Rom;\n            \n            if ((romBytes[LoromSettingOffset] & 0xEC) == 0x20)\n                return (romBytes[LoromSettingOffset + 1] & 0xF0) == 0x10 ? RomMapMode.SuperFx : RomMapMode.LoRom;\n\n            if (romBytes.Count >= 0x10000 && (romBytes[HiromSettingOffset] & 0xEF) == 0x21)\n                return RomMapMode.HiRom;\n\n            if (romBytes.Count >= 0x10000 && (romBytes[HiromSettingOffset] & 0xE7) == 0x22)\n                return RomMapMode.SuperMmc;\n\n            if (romBytes.Count >= 0x410000 && (romBytes[ExhiromSettingOffset] & 0xEF) == 0x25)\n                return RomMapMode.ExHiRom;\n\n            // all detection failed. let's pick a reasonable default, this is now jut a guess\n            detectedValidRomMapType = false;\n            // return romBytes.Count > 0x40000 ? RomMapMode.ExLoRom : RomMapMode.LoRom; // this is probably fine but, let's just be safe and pick lorom\n            return RomMapMode.LoRom;\n        }\n\n        public static bool DetectRomMapModeBustedGames(IReadOnlyList<byte> romBytes, out RomMapMode detectedRomMapMode, out RomSpeed detectedRomSpeed)\n        {\n            detectedRomMapMode = RomMapMode.LoRom; // default\n            detectedRomSpeed = RomSpeed.Unknown;\n            \n            // ReSharper disable once InvertIf\n            if (romBytes.Count >= 0x7FFF)\n            {\n                var gameNameFromRomBytes = GetCartridgeTitleFromRom(romBytes, LoromSettingOffset);\n                \n                // Contra3 overflowed it's buffer and uses 0x16 chars (1 over the max limit of 0x15).\n                // the final character \"S\" in \"CONTRA3 THE ALIEN WARS\" is incorrectly reported as the map (\"S\" = 0x53).\n                // this is invalid and confuses everything. it SHOULD be 0x20 for LoRom + SlowRom, but, the official \n                // gamedata has a broken header. wild.\n                // ReSharper disable once InvertIf\n                if (gameNameFromRomBytes == \"CONTRA3 THE ALIEN WAR\" && romBytes[LoromSettingOffset] == 0x53)\n                {\n                    detectedRomMapMode = RomMapMode.LoRom;\n                    detectedRomSpeed = RomSpeed.SlowRom;    // YES, SlowRom, not fastrom. matches japanese version. fastrom bit being set in 0x53 is a LIE\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        public static int GetRomSettingOffset(RomMapMode mode)\n        {\n            return mode switch\n            {\n                RomMapMode.LoRom => LoromSettingOffset,\n                RomMapMode.HiRom => HiromSettingOffset,\n                RomMapMode.ExHiRom => ExhiromSettingOffset,\n                RomMapMode.ExLoRom => ExloromSettingOffset,\n                _ => LoromSettingOffset\n            };\n        }\n\n        public static string PointToString(InOutPoint point)\n        {\n            string result;\n\n            if ((point & InOutPoint.EndPoint) == InOutPoint.EndPoint) result = \"X\";\n            else if ((point & InOutPoint.OutPoint) == InOutPoint.OutPoint) result = \"<\";\n            else result = \" \";\n\n            result += ((point & InOutPoint.ReadPoint) == InOutPoint.ReadPoint) ? \"*\" : \" \";\n            result += ((point & InOutPoint.InPoint) == InOutPoint.InPoint) ? \">\" : \" \";\n\n            return result;\n        }\n\n        public static string BoolToSize(bool b)\n        {\n            return b ? \"8\" : \"16\";\n        }\n\n        // read a fixed length string from an array of bytes. does not check for null termination\n        public static string ReadStringFromByteArray(byte[] bytes, int count, int offset)\n        {\n            var myName = \"\";\n            for (var i = 0; i < count; i++)\n                myName += (char)bytes[offset + i];\n\n            return myName;\n        }\n\n        /// <summary>\n        /// Read a ROM file from disk. discard the SMC header of 0x200 bytes if it exists.\n        /// </summary>\n        /// <param name=\"filename\">Rom filename to read</param>\n        /// <returns>Raw bytes</returns>\n        /// <exception cref=\"InvalidDataException\"></exception>\n        [NotNull]\n        public static byte[] ReadRomFileBytes(string filename) => \n            RemoveSmcHeader(File.ReadAllBytes(filename));\n\n        /// <summary>\n        /// Take all ROM file bytes from disk, remove SMC header if present\n        /// </summary>\n        /// <param name=\"allFileBytes\"></param>\n        /// <returns></returns>\n        /// <exception cref=\"InvalidDataException\"></exception>\n        private static byte[] RemoveSmcHeader(byte[] allFileBytes)\n        {\n            var rom = new byte[allFileBytes.Length & 0x7FFFFC00];\n\n            if ((allFileBytes.Length & 0x3FF) == 0x200)\n                // skip and dont include the SMC header\n                for (var i = 0; i < rom.Length; i++)\n                    rom[i] = allFileBytes[i + 0x200];\n            else if ((allFileBytes.Length & 0x3FF) != 0)\n                throw new InvalidDataException(\"This ROM has an unusual size. It can't be opened.\");\n            else\n                rom = allFileBytes;\n\n            return rom.Length < 0x8000 ? throw new InvalidDataException(\"This ROM is too small. It can't be opened.\") : rom;\n        }\n\n        public static Dictionary<int, FlagType> GenerateHeaderFlags(int romSettingsOffset, IReadOnlyList<byte> romBytes)\n        {\n            var flags = new Dictionary<int, FlagType>();\n\n            if (romSettingsOffset == -1)\n                return flags;\n            \n            // cart title: either ASCII or shiftJIS (japanese) encoded    x21 bytes\n            for (var i = 0; i < LengthOfTitleName; i++)\n                flags.Add(romSettingsOffset - LengthOfTitleName + i, FlagType.Text);\n            \n            // bunch of 1-byte fields   x7 bytes\n            for (var i = 0; i < 7; i++) \n                flags.Add(romSettingsOffset + i, FlagType.Data8Bit);\n            \n            // checksum (2 bytes each)  x2 words\n            for (var i = 0; i < 4; i++) \n                flags.Add(romSettingsOffset + 7 + i, FlagType.Data16Bit);\n            \n            // vector table entries (we'll add labels to each of these later). 2x bytes each x 16 entries\n            for (var i = 0; i < 0x20; i++) \n                flags.Add(romSettingsOffset + 11 + i, FlagType.Pointer16Bit);\n            \n            if (romBytes[romSettingsOffset - 1] == 0)\n            {\n                flags.Remove(romSettingsOffset - 1);\n                flags.Add(romSettingsOffset - 1, FlagType.Data8Bit);\n                for (var i = 0; i < 0x10; i++) \n                    flags.Add(romSettingsOffset - 0x25 + i, FlagType.Data8Bit);\n            }\n            else if (romBytes[romSettingsOffset + 5] == 0x33)\n            {\n                for (var i = 0; i < 6; i++) \n                    flags.Add(romSettingsOffset - 0x25 + i, FlagType.Text);\n\n                for (var i = 0; i < 10; i++) \n                    flags.Add(romSettingsOffset - 0x1F + i, FlagType.Data8Bit);\n            }\n\n            return flags;\n        }\n\n        public const int LengthOfTitleName = 0x15;\n\n        #if DIZ_3_BRANCH\n        public static ByteSourceMapping CreateRomMappingFromRomByteSource(ByteSource romByteSource, RomMapMode romMapMode, RomSpeed romSpeed)\n        {\n            return new()\n            {\n                ByteSource = romByteSource,\n                RegionMapping = new RegionMappingSnesRom\n                {\n                    RomSpeed = romSpeed,\n                    RomMapMode = romMapMode,\n                }\n            };\n        }\n\n        public static ByteSourceMapping CreateRomMappingFromRomRawBytes(\n            IReadOnlyCollection<byte> actualRomBytes, RomMapMode romMapMode, RomSpeed romSpeed)\n        {\n            var data = actualRomBytes.Select(b => new ByteEntry() {Byte = b}).ToList();\n\n            var romByteSource = new ByteSource\n            {\n                Bytes = new StorageList<ByteEntry>(data),\n                Name = \"Snes ROM\"\n            };\n            \n            return CreateRomMappingFromRomByteSource(romByteSource, romMapMode, romSpeed);\n        }\n\n        public static ByteSource CreateSnesAddressSpace()\n        {\n            const int snesAddressableBytes = 0x1000000;\n            return new ByteSource\n            {\n                Bytes = new StorageSparse<ByteEntry>(snesAddressableBytes),\n                Name = \"SNES Main Cpu BUS\",\n            };\n        }\n        #endif\n        \n        public static bool IsLocationPoint(this IInOutPointGettable data, int pointer, InOutPoint mustHaveFlag) =>\n            (data.GetInOutPoint(pointer) & mustHaveFlag) != 0;\n\n        public static bool IsLocationAnEndPoint(this IInOutPointGettable data, int pointer) => \n            IsLocationPoint(data, pointer, InOutPoint.EndPoint);\n        \n        public static bool IsLocationAReadPoint(this IInOutPointGettable data, int pointer) => \n            IsLocationPoint(data, pointer, InOutPoint.ReadPoint);\n\n        // Take a WRAM address (from 0x0 to 0x1FFFF) and convert it to the equivalent\n        // main SNES address between $7E0000 through $7FFFFF\n        public static int GetSnesAddressFromWramAddress(int wramAddress)\n        {\n            if (wramAddress is < 0 or > 0x1FFFF)\n                return -1;\n            \n            return wramAddress + 0x7E0000;\n        }\n\n        // This takes a SNES address and returns the offset into WRAM address space, if it exists. It deals with mirroring\n        // valid return ranges are 0 through 0x1FFFFF (the offset into WRAM address space, NO LONGER in SNES address space)\n        // return -1 if it doesn't map to any offset in WRAM address space\n        // NOTE: this is the offset in WRAM, and not it's mirrored value.\n        // i.e. if you give it $0013, this function will return $0013, and **NOT** $7E0013\n        public static int GetWramAddressFromSnesAddress(int snesAddress)\n        {\n            if (snesAddress == -1)\n                return -1;\n            \n            // option 1:\n            // Primary WRAM range is mapped directly into SNES address space: 0x7E0000 - 0x7FFFFF\n            // this directly maps 1:1 into WRAM address space\n            if (snesAddress is >= 0x7E0000 and <= 0x7FFFFF)\n            {\n                return snesAddress - 0x7E0000;\n            }\n            \n            // option 2:\n            // WRAM is mapped into certain ranges in banks $00-$3F and $80-$BF\n            var bank = (snesAddress >> 16) & 0xFF; // Extract the high byte (bank number)\n\n            // Check if the bank is within WRAM-mirroring ranges: $00-$3F or $80-$BF\n            var bankContainsWramMirror = bank is (>= 0x00 and <= 0x3F) or (>= 0x80 and <= 0xBF);\n            if (!bankContainsWramMirror)\n            {\n                return -1;\n            }\n            \n            // if it's in a bank with a WRAM mirror, is the 1st 16bits of address in range for WRAM?\n            var addrNoBank = snesAddress & 0x00FFFF;\n            if (addrNoBank > 0x1FFF)\n                return -1; // out of range\n\t\n            return addrNoBank; // in range!\n        }\n        \n        public static bool AreLabelsSameMirror(int snesAddress, int labelAddress)\n        {\n            if (snesAddress == -1 || labelAddress == -1)\n                return false;\n\n            // early out shortcut \n            if ((snesAddress & 0xFFFF) != (labelAddress & 0xFFFF))\n                return false;\n            \n            // this function is a crappy and probably error-prone way to do this. gotta start somewhere.\n            // it would be better to do this by mapping out the memory regions than trying to go backwards\n            // from any arbitrary SNES address back to the mapped region. still, we'll give it a shot.\n            // we MOST care about things affecting labels that humans care about: WRAM mirrors and SNES registers\n            \n            // check WRAM mirroring\n            if (AreSnesAddressesSameMirroredWramAddresses(snesAddress, labelAddress))\n                return true;\n\n            // check other IO mirroring (overlaps with above for LowRAM too, but that's OK)\n            if (AreSnesAddressesSameMirroredIoRegion(snesAddress, labelAddress)) \n                return true;\n\n            return false;\n        }\n        \n        // if a SNES addres is mapped to WRAM, ensure the address is in the primary mapped region.\n        // if not, or if it's already normalized to that region, just return the original address\n        // example: $0000FE will return $7E00FE\n        public static int NormalizeSnesWramAddress(int snesAddress)\n        {\n            var normalizedSnesAddress = GetSnesAddressFromWramAddress(GetWramAddressFromSnesAddress(snesAddress));\n            return normalizedSnesAddress != -1 ? normalizedSnesAddress : snesAddress;\n        }\n\n        public static bool AreSnesAddressesSameMirroredIoRegion(int snesAddress1, int snesAddress2)\n        {\n            var reducedSnesAddr1 = GetUnmirroredIoRegionFromBank(snesAddress1);\n            var reducedSnesAddr2 = GetUnmirroredIoRegionFromBank(snesAddress2);\n            \n            return reducedSnesAddr1 != -1 && reducedSnesAddr2 == reducedSnesAddr1;\n        }\n\n        public static int GetUnmirroredIoRegionFromBank(int snesAddress)\n        {\n            if (snesAddress == -1)\n                return -1;\n            \n            // Mirrored WRAM range in banks $00-$3F and $80-$BF\n            var bank = (snesAddress >> 16) & 0xFF; // Extract the high byte (bank number)\n\n            // Check if the bank is within WRAM-mirroring ranges: $00-$3F or $80-$BF\n            var bankContainsMirror = bank is (>= 0x00 and <= 0x3F) or (>= 0x80 and <= 0xBF);\n            if (!bankContainsMirror) \n                return -1;\n            \n            // are we in the mirrored region?\n            var low16Addr = snesAddress & 0xFFFF;\n            if (low16Addr is < 0x0000 or > 0x7FFF) \n                return -1;\n\n            return low16Addr;\n        }\n\n            \n        public static bool AreSnesAddressesSameMirroredWramAddresses(int snesAddress1, int snesAddress2)\n        {\n            var wramAddress1 = GetWramAddressFromSnesAddress(snesAddress1);\n            var wramAddress2 = GetWramAddressFromSnesAddress(snesAddress2);\n        \n            return wramAddress1 != -1 && wramAddress2 == wramAddress1;\n        }\n        \n        // detect if this is a +/- label.  like \"+\", \"-\", or \"++\", \"--\" etc.\n        public static bool IsValidPlusMinusLabel(string label)\n        {\n            if (string.IsNullOrEmpty(label))\n                return false;\n        \n            var firstChar = label[0];\n            if (firstChar != '+' && firstChar != '-')\n                return false;\n        \n            for (var i = 1; i < label.Length; i++)\n            {\n                if (label[i] != firstChar)\n                    return false;\n            }\n    \n            return true;\n        }\n        \n        public static string ConvertNumToHexStr(int num, int numByteDigitsToDisplay)\n        {\n            num &= ~(-1 << (8 * numByteDigitsToDisplay));\n            return Util.NumberToBaseString(num, Util.NumberBase.Hexadecimal, 2 * numByteDigitsToDisplay, true);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core/util/ServiceProvider.cs",
    "content": "﻿using LightInject;\n\nnamespace Diz.Core.util;\n\npublic static class DizServiceProvider\n{\n    // Thou shalt not create a global instance of this class, for verily thine unit tests.....\n    // \n    // Don't cache this. Use IServiceContainer first thing at app startup to register services.\n    // Then cast it down only to IServiceFactory and don't register anything after the first class is used.\n    public static IServiceContainer CreateServiceContainer()\n    {\n        var containerOptions = new ContainerOptions\n        {\n            EnablePropertyInjection = false,\n        };\n        return new ServiceContainer(containerOptions);\n    }   \n}"
  },
  {
    "path": "Diz.Core/util/Util.cs",
    "content": "﻿#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.Globalization;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Net.Sockets;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing Diz.Core.Interfaces;\nusing JetBrains.Annotations;\n\nnamespace Diz.Core.util;\n\npublic static class Util\n{\n    public static void RemoveAll<TItem>(this ICollection<TItem> collection, Predicate<TItem> match)\n    {\n        if (match == null)\n            throw new ArgumentNullException(nameof(match));\n\n        // make a copy to avoid removing items from the list we're enumerating\n        collection.Where(item => match(item))\n            .ToList()\n            .ForEach(item => collection.Remove(item));\n    }\n        \n    public static void AddRange<TItem>(this ICollection<TItem> collection, IEnumerable<TItem> newItems)\n    {\n        foreach (var newItem in newItems) \n            collection.Add(newItem);\n    }\n        \n    // https://stackoverflow.com/questions/703281/getting-path-relative-to-the-current-working-directory/703290#703290\n    public static string GetRelativePath(string fileSpec, string? folder)\n    {\n        var pathUri = new Uri(fileSpec);\n        // Folders must end in a slash\n        if (!string.IsNullOrEmpty(folder) && !folder.EndsWith(Path.DirectorySeparatorChar.ToString()))\n        {\n            folder += Path.DirectorySeparatorChar;\n        }\n        var folderUri = new Uri(folder ?? \"\");\n        return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));\n    }\n        \n    public static string TryGetRelativePath(string fileSpec, string? folder = null)\n    {\n        if (string.IsNullOrEmpty(folder))\n            return fileSpec;\n            \n        try\n        {\n            return GetRelativePath(fileSpec, folder);\n        }\n        catch (Exception)\n        {\n            return fileSpec;\n        }\n    }\n        \n    public static string? GetDirNameOrEmpty(string path) => \n        string.IsNullOrEmpty(path) ? \"\" : Path.GetDirectoryName(path);\n\n    public enum NumberBase\n    {\n        Decimal = 3, Hexadecimal = 2, Binary = 8\n    }\n\n    public static string NumberToBaseString(int v, NumberBase noBase, int d = -1, bool showPrefix = false)\n    {\n        var digits = d < 0 ? (int)noBase : d;\n        switch (noBase)\n        {\n            case NumberBase.Decimal:\n                return digits == 0 ? v.ToString(\"D\") : v.ToString(\"D\" + digits);\n            case NumberBase.Hexadecimal:\n                if (digits == 0) return v.ToString(\"X\");\n                return (showPrefix ? \"$\" : \"\") + v.ToString(\"X\" + digits);\n            case NumberBase.Binary:\n                var b = \"\";\n                var i = 0;\n                while (digits == 0 ? v > 0 : i < digits)\n                {\n                    b += (v & 1);\n                    v >>= 1;\n                    i++;\n                }\n                return (showPrefix ? \"%\" : \"\") + b;\n        }\n        return \"\";\n    }\n        \n    public static string ToHexString6(int i)\n    {\n        return NumberToBaseString(i, NumberBase.Hexadecimal, 6);\n    }\n        \n    public static int ParseHexOrBase10String(string data)\n    {\n        var hex = false;\n        var hexDigitStartIndex = 0;\n\n        if (data.Length > 1)\n        {\n            hex = data[0] == '$';\n            hexDigitStartIndex = 1;\n        }\n        else if (data.Length > 2)\n        {\n            hexDigitStartIndex = 2;\n            hex =\n                data[0] == '0' && data[1] == 'x' ||\n                data[0] == '#' && data[1] == '$';\n        }\n\n        if (hex)\n            return (int)ByteUtil.ByteParseHex(data, hexDigitStartIndex, data.Length - hexDigitStartIndex);\n\n        return int.Parse(data);\n    }\n\n    // this function is a little weird and redundant maybe?\n    public static IEnumerable<string> ReadLines(string path)\n    {\n        using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan);\n        using var sr = new StreamReader(fs, Encoding.UTF8);\n\n        while (sr.ReadLine() is { } line)\n        {\n            yield return line;\n        }\n    }\n\n    public static long GetFileSizeInBytes(string filename)\n    {\n        var fi = new FileInfo(filename);\n        if (!fi.Exists)\n            return -1;\n\n        return fi.Length;\n    }\n\n    // https://stackoverflow.com/questions/33119119/unzip-byte-array-in-c-sharp\n    public static byte[]? TryUnzip(byte[] data)\n    {\n        try\n        {\n            using var comp = new MemoryStream(data);\n            using var gzip = new GZipStream(comp, CompressionMode.Decompress);\n            using var res = new MemoryStream();\n            gzip.CopyTo(res);\n            return res.ToArray();\n        }\n        catch (Exception)\n        {\n            return null;\n        }\n    }\n\n    public static byte[]? TryZip(byte[] data)\n    {\n        try\n        {\n            using var comp = new MemoryStream();\n            using var gzip = new GZipStream(comp, CompressionMode.Compress);\n            gzip.Write(data, 0, data.Length);\n            gzip.Close();\n            return comp.ToArray();\n        }\n        catch (Exception)\n        {\n            return null;\n        }\n    }\n\n    public static byte[] ReadNext(Stream stream, int count, out int bytesRead)\n    {\n        var buffer = new byte[count];\n        bytesRead = ReadNext(stream, buffer, count);\n        return buffer;\n    }\n\n    public static int ReadNext(Stream stream, byte[] buffer, int count)\n    {\n        // not in love with this.\n        return DoReadNext(stream, buffer, count, stream is NetworkStream);\n    }\n\n    private static int DoReadNext(Stream stream, byte[] buffer, int count, bool continueOnZeroBytesRead=false)\n    {\n        var offset = 0;\n\n        while (count > 0)\n        {\n            var bytesRead = stream.Read(buffer, offset, count);\n                \n            count -= bytesRead;\n            offset += bytesRead;\n\n            if (bytesRead == 0 && !continueOnZeroBytesRead)\n                break;\n        }\n\n        if (count > 0)\n            throw new EndOfStreamException();\n\n        return offset;\n    }\n\n    public static string GetEnumDescription(Enum value)\n    {\n        // example:\n        // type = Data.ROMMapMode (the entire enum)\n        // value = ExSA1ROM (one particular entry from the enum)\n        // description = \"SA-1 ROM (FuSoYa's 8MB mapper)\",   (contents of [Description] attribute)\n\n        return GetEnumAttribute(\n            value, \n            (DescriptionAttribute d) => d?.Description\n        ) ?? value.ToString();\n    }\n\n    public static TResult? GetEnumAttribute<TAttribute, TResult>(object value, Func<TAttribute, TResult> getValueFn) where TAttribute : Attribute\n    {\n        return GetFieldAttribute(getValueFn, value.GetType(), value.ToString());\n    }\n\n    public static TResult? GetFieldAttribute<TAttribute, TResult>(Func<TAttribute, TResult> getValueFn, Type type, string? memberName)\n        where TAttribute : Attribute\n    {\n        var memberInfo = memberName!=null ? type.GetField(memberName) : null;\n        if (memberInfo == null)\n            return default;\n            \n        var attr = (TAttribute?)Attribute.GetCustomAttribute(memberInfo, typeof(TAttribute));\n        return attr!=null ? getValueFn(attr) : default;\n    }\n        \n    public static TResult? GetPropertyAttribute<TAttribute, TResult>(Func<TAttribute, TResult> getValueFn, Type type, string? propertyName)\n        where TAttribute : Attribute\n    {\n        var property = propertyName!=null ? type.GetProperty(propertyName) : null;\n        if (property == null)\n            return default;\n            \n        var attr = (TAttribute?)Attribute.GetCustomAttribute(property, typeof(TAttribute));\n        return attr != null ? getValueFn(attr) : default;\n    }\n\n    // take a enum type that has [Description] attributes,\n    // return a List with with kvp pairs of enum vs description\n    public static List<KeyValuePair<TEnum, string>> GetEnumDescriptions<TEnum>() where TEnum : Enum\n    {\n        return GetEnumInfo<TEnum, string>((value) => GetEnumDescription(value));\n    }\n\n    // perf: might be a little slow, caution when in tight loops\n    public static List<KeyValuePair<TEnum, Color>> GetEnumColorDescriptions<TEnum>() where TEnum : Enum\n    {\n        return GetEnumInfo<TEnum, Color>((value) => GetEnumColor(value));\n    }\n    \n    private static Color GetEnumColor(Enum value)\n    {\n        var colorAttr = GetEnumAttribute(\n            value,\n            (ColorDescriptionAttribute d) => d\n        );\n\n        if (colorAttr == null)\n            return Color.Black;\n\n        var hex = colorAttr.HexColor;\n        var r = (byte)((hex >> 16) & 0xFF);\n        var g = (byte)((hex >> 8) & 0xFF);\n        var b = (byte)(hex & 0xFF);\n    \n        return Color.FromArgb(r, g, b);\n    }\n\n    public static List<KeyValuePair<TEnum, TType>> GetEnumInfo<TEnum, TType>(Func<TEnum, TType> getValue) where TEnum : Enum\n    {\n        var type = typeof(TEnum);\n        return Enum.GetValues(type)\n            .Cast<TEnum>()\n            .Select(value => new\n                KeyValuePair<TEnum, TType>(key: value, value: getValue(value))\n            )\n            .OrderBy(item => item.Key)\n            .ToList();\n    }\n\n    // sadly, this entire conversion is a bit slow so, cache it as we look it up\n    private static readonly Dictionary<FlagType, Color> CachedRomFlagColors = new();\n\n    public static Color GetColorFromFlag(FlagType romFlag)\n    {\n        if (CachedRomFlagColors.TryGetValue(romFlag, out var color))\n            return color;\n\n        color = GetEnumColor(romFlag);\n        CachedRomFlagColors[romFlag] = color;\n\n        return color;\n    }\n        \n    public static (string stdout, string stderr) RunCommandGetOutput(string cmdExePath, string args)\n    {\n        var process = new Process\n        {\n            StartInfo =\n            {\n                FileName = cmdExePath,\n                Arguments = args,\n                UseShellExecute = false,\n                RedirectStandardOutput = true,\n                RedirectStandardError = true\n            }\n        };\n        process.Start();\n            \n        var output = process.StandardOutput.ReadToEnd();\n        Console.WriteLine(output);\n            \n        var err = process.StandardError.ReadToEnd();\n        Console.WriteLine(err);\n            \n        process.WaitForExit();\n\n        return (output, err);\n    }\n\n    public static void OpenExternalProcess(string args)\n    {\n        var info = new ProcessStartInfo(args) { UseShellExecute = true };\n        Process.Start(info);\n    }\n\n    // clamp index so index >= 0 and index < size\n    // for arrays\n    public static int ClampIndex(int index, int size) => Clamp(index, 0, size - 1);\n        \n    // returns i if i >= min and index <= max, otherwise returns min or max\n    public static int Clamp(int i, int min, int max)\n    {\n        if (min < 0 || min > max)\n            throw new IndexOutOfRangeException(\"ClampIndex params not in range\");\n            \n        return i > max \n            ? max\n            : i < min\n                ? min\n                : i;\n    }\n\n    public static bool IsBetween(int i, int max) => i >= 0 && i <= max;\n    public static bool IsBetween(int i, int min, int max) => i >= min && i <= max;\n\n    public static void SplitOnFirstComma(string instr, out string firstPart, out string remainder)\n    {\n        if (!instr.Contains(\",\"))\n        {\n            firstPart = instr;\n            remainder = \"\";\n            return;\n        }\n\n        firstPart = instr.Substring(0, instr.IndexOf(','));\n        remainder = instr.Substring(instr.IndexOf(',') + 1);\n    }\n\n    public static string LeftAlign(int length, string str) => string.Format(GetLeftAlignFormatStr(length), str);\n\n    public static string GetLeftAlignFormatStr(int length) => $\"{{0,{-length}}}\";\n        \n    public static string ReadManifestData(Assembly assembly, string resourceName)\n    {\n        resourceName = resourceName.Replace(\"/\", \".\");\n        using var stream = assembly.GetManifestResourceStream(resourceName);\n            \n        if (stream == null)\n            throw new InvalidOperationException(\"Could not load manifest resource stream.\");\n\n        return new StreamReader(stream).ReadToEnd();\n    }\n\n    // decent helper check before doing a deep compare on two lists.\n    // considers them equal if any are true:\n    // - both lists are null\n    // - if one list is non-null, and the other contains zero elements\n    public static bool BothListsNullOrContainNoItems<T>(ICollection<T> c1, ICollection<T> c2)\n    {\n        return c1 switch\n        {\n            null when c2 == null => true,\n            null => c2.Count == 0,\n            not null when c2 == null => c1.Count == 0,\n            _ => false\n        };\n    }\n        \n    public static bool CollectionsBothEmptyOrEqual<T>(ICollection<T> c1, ICollection<T> c2)\n    {\n        if (BothListsNullOrContainNoItems(c1, c2)) \n            return true;\n\n        return c1?.SequenceEqual(c2) ?? false;\n    }\n\n    /// <summary>\n    /// Chop any leading zeroes from a hex string (must start with '$')\n    /// example: turn \"$0004\" or \"$000004 into \"$04\"\n    /// </summary>\n    /// <param name=\"hexString\">input hex string</param>\n    /// <returns>if hex string, same string with any pairs of extra leading zeroes removed. if not hex string, return original string</returns>\n    public static string ChopExtraZeroesFromHexStr(string hexString)\n    {\n        // if it's a hex number (prefixed with a $) then, trim the leading zeroes\n        if (!hexString.StartsWith('$')) \n            return hexString;\n            \n        // remove $\n        var numbersOnly = hexString[1..];\n            \n        while (numbersOnly.Length > 2 && numbersOnly.StartsWith(\"00\")) {\n            numbersOnly = numbersOnly[2..]; // cut leading zeroes\n        }\n\n        return $\"${numbersOnly}\";\n    }\n}\n    \n    \n// makes it a little easier to deal with INotifyPropertyChanged in derived classes\npublic interface INotifyPropertyChangedExt : INotifyPropertyChanged\n{\n    // would be great if this didn't have to be public. :shrug:\n    void OnPropertyChanged(string propertyName);\n}\n    \npublic static class NotifyPropertyChangedExtensions\n{\n    /// <summary>\n    /// Set a field, and if changed, dispatch any events associated with it\n    /// </summary>\n    /// <returns>true if we set property to a new value and dispatched events</returns>\n    public static bool SetField<T>(this INotifyPropertyChanged sender, [CanBeNull] PropertyChangedEventHandler handler, ref T field, T value, bool compareRefOnly = false, [CallerMemberName] string propertyName = null)\n    {\n        if (FieldIsEqual(field, value, compareRefOnly)) \n            return false;\n            \n        field = value;\n            \n        handler?.Invoke(sender, new PropertyChangedEventArgs(propertyName));\n        return true;\n    }\n        \n    /// <summary>\n    /// Set a field, and if changed, dispatch any events associated with it\n    /// </summary>\n    /// <returns>true if we set property to a new value and dispatched events</returns>\n    public static bool SetField<T>(this INotifyPropertyChangedExt sender, ref T field, T value, bool compareRefOnly = false, [CallerMemberName] string propertyName = null)\n    {\n        if (FieldIsEqual(field, value, compareRefOnly)) \n            return false;\n            \n        field = value;\n            \n        sender.OnPropertyChanged(propertyName);\n        return true;\n    }\n\n    /// <summary>\n    /// Test if one field is equal to another\n    /// </summary>\n    /// <returns>true if we equal</returns>\n    public static bool FieldIsEqual<T>(T field, T value, bool compareRefOnly = false)\n    {\n        if (compareRefOnly)\n        {\n            if (ReferenceEquals(field, value))\n                return true;\n        }\n        else if (EqualityComparer<T>.Default.Equals(field, value))\n        {\n            return true;\n        }\n\n        return false;\n    }\n}\n\npublic static class ContentUtils\n{\n    public static object? SingleOrDefaultOfType<T>(this IEnumerable<T?> enumerable, Type desiredType)\n    {\n        return enumerable.SingleOrDefault(item => item?.GetType() == desiredType);\n    }\n    \n    public static TDesired? SingleOrDefaultOfType<TDesired, T>(this IEnumerable<T> enumerable)\n    {\n        return enumerable.OfType<TDesired>().SingleOrDefault();\n    }\n}"
  },
  {
    "path": "Diz.Core/util/WorkerTaskManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Diz.Core.util\n{\n    public class WorkerTaskManager : IWorkerTaskManager\n    {\n        private readonly List<Task> tasks = [];\n        private readonly object taskLock = new();\n        private readonly ManualResetEvent notFinishing = new(false);\n        \n        private volatile bool finished;\n        private Timer timer;\n        private readonly object timerLock = new();\n\n        public void Start()\n        {\n            var oneSecond = TimeSpan.FromSeconds(1);\n            timer = new Timer(_ => Update(), null, oneSecond, oneSecond);\n        }\n\n        public void StartFinishing()\n        {\n            notFinishing.Set();\n        }\n\n        private void Update()\n        {\n            lock (timerLock)\n            {\n                if (timer == null)\n                    return;\n\n                if (finished)\n                {\n                    timer.Change(-1, -1);\n                    timer.Dispose();\n                    timer = null;\n                }\n\n                lock (taskLock)\n                {\n                    Debug.Assert(!tasks.Contains(null));\n                    tasks.RemoveAll(task => task.IsCompleted);\n                    Debug.Assert(!tasks.Contains(null));\n                }\n            }\n        }\n\n        public Task Run(Action action, CancellationToken cancelToken)\n        {\n            return StartTask(new Task(action, cancelToken));\n        }\n        \n        public Task Run(Action action)\n        {\n            return StartTask(new Task(action));\n        }\n\n        private Task StartTask(Task task)\n        {\n            Debug.Assert(task != null);\n\n            lock (taskLock)\n            {\n                tasks.Add(task);\n                Debug.Assert(!tasks.Contains(null));\n            }\n\n            task.Start();\n\n            return task;\n        }\n\n        // blocking method\n        public void WaitForAllTasksToComplete()\n        {\n            // wait until we've been told we should be finishing\n            notFinishing.WaitOne();\n\n            while (true)\n            {\n                List<Task> tasksCopy;\n                lock (taskLock)\n                {\n                    if (tasks.Count == 0)\n                        break;\n\n                    Debug.Assert(!tasks.Contains(null));\n                    tasksCopy = new List<Task>(tasks);\n                    Debug.Assert(!tasksCopy.Contains(null));\n                }\n\n                // anything in our original list, let it run to completion\n                Task.WaitAll(tasksCopy.ToArray());\n            }\n\n            finished = true;\n        }\n    }\n\n    // reference implementation that runs synchronously. mostly for benchmarking/etc.\n    public class WorkerTaskManagerSynchronous : IWorkerTaskManager\n    {\n        public void Start() { }\n        \n        public Task Run(Action action, CancellationToken cancelToken)\n        {\n            var syncTask = new Task(action);\n            syncTask.RunSynchronously();\n            return syncTask;\n        }\n\n        public Task Run(Action action)\n        {\n            return Run(action, new CancellationToken());\n        }\n\n        public void WaitForAllTasksToComplete() { }\n        public void StartFinishing() { }\n    }\n\n    public interface IWorkerTaskManager\n    {\n        void Start();\n        \n        void WaitForAllTasksToComplete();\n        \n        Task Run(Action action, CancellationToken cancelToken);\n        Task Run(Action action);\n\n        void StartFinishing();\n    }\n}"
  },
  {
    "path": "Diz.Core/vendor/LightInject.AutoFactory.cs",
    "content": "﻿/*********************************************************************************\n    The MIT License (MIT)\n\n    Copyright (c) 2016 bernhard.richter@gmail.com\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE.\n******************************************************************************\n    LightInject.AutoFactory version 2.0.0\n    http://www.lightinject.net/\n    http://twitter.com/bernhardrichter\n******************************************************************************/\n\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"StyleCop.CSharp.ReadabilityRules\", \"SA1126:PrefixCallsCorrectly\", Justification = \"Reviewed\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"StyleCop.CSharp.ReadabilityRules\", \"SA1101:PrefixLocalCallsWithThis\", Justification = \"No inheritance\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"StyleCop.CSharp.MaintainabilityRules\", \"SA1402:FileMayOnlyContainASingleClass\", Justification = \"Single source file deployment.\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"StyleCop.CSharp.DocumentationRules\", \"SA1633:FileMustHaveHeader\", Justification = \"Custom header.\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"StyleCop.CSharp.DocumentationRules\", \"SA1600:ElementsMustBeDocumented\", Justification = \"All public members are documented.\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"StyleCop.CSharp.NamingRules\", \"SA1300:ElementMustBeginWithUpperCaseLetter\", Justification = \"Product name starts with lowercase letter.\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"MaintainabilityRules\", \"SA1403\", Justification = \"One source file\")]\n[module: System.Diagnostics.CodeAnalysis.SuppressMessage(\"DocumentationRules\", \"SA1649\", Justification = \"One source file\")]\n\nnamespace LightInject\n{\n    using System;\n    using AutoFactory;\n\n    /// <summary>\n    /// Extends the <see cref=\"IServiceContainer\"/> interface with\n    /// a method that makes it possible to register factory interfaces\n    /// that is automatically implemented.\n    /// </summary>\n    public static class ContainerExtensions\n    {\n        /// <summary>\n        /// Enables LightInject to automatically implement factory interfaces.\n        /// </summary>\n        /// <param name=\"container\">The target <see cref=\"IServiceContainer\"/>.</param>\n        public static void EnableAutoFactories(this IServiceRegistry container)\n        {\n            container.Register<IAutoFactoryBuilder, AutoFactoryBuilder>();\n            container.Register<ITypeBuilderFactory, TypeBuilderFactory>(new PerContainerLifetime());\n            container.Register<IServiceNameResolver, ServiceNameResolver>(new PerContainerLifetime());\n        }\n\n        /// <summary>\n        /// Registers a factory of type <typeparamref name=\"TFactory\"/> to be implemented.\n        /// </summary>\n        /// <typeparam name=\"TFactory\">The type of factory to be implemented.</typeparam>\n        /// <param name=\"container\">The target <see cref=\"IServiceRegistry\"/>.</param>\n        public static void RegisterAutoFactory<TFactory>(this IServiceRegistry container)\n        {\n            container.Register(CreateFactoryInstance<TFactory>, new PerContainerLifetime());\n        }\n\n        private static TFactory CreateFactoryInstance<TFactory>(IServiceFactory container)\n        {\n            IAutoFactoryBuilder builder = container.GetInstance<IAutoFactoryBuilder>();\n            Type factoryType = builder.GetFactoryType(typeof(TFactory));\n            return (TFactory)Activator.CreateInstance(factoryType, container);\n        }\n    }\n}\n\nnamespace LightInject.AutoFactory\n{\n    using System;\n    using System.Linq;\n    using System.Reflection;\n    using System.Reflection.Emit;\n    using System.Text.RegularExpressions;\n\n    /// <summary>\n    /// Represents a class that is capable of creating a\n    /// factory type that implements a given factory interface.\n    /// </summary>\n    public interface IAutoFactoryBuilder\n    {\n        /// <summary>\n        /// Gets a <see cref=\"Type\"/> that implements the given <paramref name=\"factoryInterface\"/>.\n        /// </summary>\n        /// <param name=\"factoryInterface\">The interface to be implemented by the factory type.</param>\n        /// <returns>A factory type that implements the <paramref name=\"factoryInterface\"/>.</returns>\n        Type GetFactoryType(Type factoryInterface);\n    }\n\n    /// <summary>\n    /// Represents a class that is capable of creating a <see cref=\"TypeBuilder\"/> that\n    /// is used to build the proxy type.\n    /// </summary>\n    public interface ITypeBuilderFactory\n    {\n        /// <summary>\n        /// Creates a <see cref=\"TypeBuilder\"/> instance that is used to build a proxy\n        /// type that inherits/implements the <paramref name=\"targetType\"/> with an optional\n        /// set of <paramref name=\"additionalInterfaces\"/>.\n        /// </summary>\n        /// <param name=\"targetType\">The <see cref=\"Type\"/> that the <see cref=\"TypeBuilder\"/> will inherit/implement.</param>\n        /// <param name=\"additionalInterfaces\">A set of additional interfaces to be implemented.</param>\n        /// <returns>A <see cref=\"TypeBuilder\"/> instance for which to build the proxy type.</returns>\n        TypeBuilder CreateTypeBuilder(Type targetType, Type[] additionalInterfaces);\n\n        /// <summary>\n        /// Creates a proxy <see cref=\"Type\"/> based on the given <paramref name=\"typeBuilder\"/>.\n        /// </summary>\n        /// <param name=\"typeBuilder\">The <see cref=\"TypeBuilder\"/> that represents the proxy type.</param>\n        /// <returns>The proxy <see cref=\"Type\"/>.</returns>\n        Type CreateType(TypeBuilder typeBuilder);\n    }\n\n    /// <summary>\n    /// Represents a class that is capable of resolving the\n    /// name of the service to be retrived based on a given method.\n    /// </summary>\n    public interface IServiceNameResolver\n    {\n        /// <summary>\n        /// Resolves the name of the service to be retrieved based on the given <paramref name=\"method\"/>.\n        /// </summary>\n        /// <param name=\"method\">The method for which to resolve the service name.</param>\n        /// <returns>The name of the service if the method represent a named service, otherwise null.</returns>\n        string Resolve(MethodInfo method);\n    }\n\n    /// <summary>\n    /// A class that is capable of creating a\n    /// factory type that implements a given factory interface.\n    /// </summary>\n    public class AutoFactoryBuilder : IAutoFactoryBuilder\n    {\n        private static readonly MethodInfo[] GetInstanceMethods;\n        private static readonly MethodInfo[] NamedGetInstanceMethods;\n        private static readonly ConstructorInfo ObjectConstructor;\n        private readonly IServiceNameResolver serviceNameResolver;\n        private readonly ITypeBuilderFactory typeBuilderFactory;\n\n        static AutoFactoryBuilder()\n        {\n            GetInstanceMethods =\n                typeof(ServiceFactoryExtensions).GetTypeInfo()\n                    .DeclaredMethods.Where(m => m.Name == \"GetInstance\" && m.IsGenericMethod && (m.GetParameters().Length - 1) < m.GetGenericArguments().Length)\n                    .OrderBy(m => m.GetGenericArguments().Length)\n                    .ToArray();\n\n            NamedGetInstanceMethods =\n                typeof(ServiceFactoryExtensions).GetTypeInfo()\n                    .DeclaredMethods.Where(\n                        m =>\n                            m.Name == \"GetInstance\" && m.IsGenericMethod && m.GetParameters().Length > 0 &&\n                            m.GetParameters().Last().ParameterType == typeof(string))\n                    .OrderBy(m => m.GetGenericArguments().Length)\n                    .ToArray();\n\n            ObjectConstructor =\n                typeof(object).GetTypeInfo().DeclaredConstructors.Single(c => c.GetParameters().Length == 0);\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AutoFactoryBuilder\"/> class.\n        /// </summary>\n        /// <param name=\"typeBuilderFactory\">The <see cref=\"ITypeBuilderFactory\"/> that is responsible for\n        /// creating a <see cref=\"TypeBuilder\"/> instance.</param>\n        /// <param name=\"serviceNameResolver\">The <see cref=\"IServiceNameResolver\"/> that is\n        /// responsible for resolving the service name from a given factory method.</param>\n        public AutoFactoryBuilder(ITypeBuilderFactory typeBuilderFactory, IServiceNameResolver serviceNameResolver)\n        {\n            this.serviceNameResolver = serviceNameResolver;\n            this.typeBuilderFactory = typeBuilderFactory;\n        }\n\n        /// <summary>\n        /// Gets a <see cref=\"Type\"/> that implements the given <paramref name=\"factoryInterface\"/>.\n        /// </summary>\n        /// <param name=\"factoryInterface\">The interface to be implemented by the factory type.</param>\n        /// <returns>A factory type that implements the <paramref name=\"factoryInterface\"/>.</returns>\n        public Type GetFactoryType(Type factoryInterface)\n        {\n            if (!factoryInterface.GetTypeInfo().IsInterface)\n            {\n                throw new InvalidOperationException(\"The factory interface type must be an interface\");\n            }\n\n            var typeBuilder = typeBuilderFactory.CreateTypeBuilder(factoryInterface, new Type[] { });\n            var containerField = ImplementConstructor(typeBuilder);\n            ImplementMethods(typeBuilder, factoryInterface, containerField);\n            return typeBuilderFactory.CreateType(typeBuilder);\n        }\n\n        private static FieldBuilder ImplementConstructor(TypeBuilder typeBuilder)\n        {\n            var containerField = typeBuilder.DefineField(\"container\", typeof(IServiceFactory), FieldAttributes.Private);\n            const MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName\n                                                | MethodAttributes.RTSpecialName;\n            var constructorBuilder = typeBuilder.DefineConstructor(attributes, CallingConventions.Standard, new[] { typeof(IServiceFactory) });\n\n            var il = constructorBuilder.GetILGenerator();\n            il.Emit(OpCodes.Ldarg_0);\n            il.Emit(OpCodes.Call, ObjectConstructor);\n            il.Emit(OpCodes.Ldarg_0);\n            il.Emit(OpCodes.Ldarg_1);\n            il.Emit(OpCodes.Stfld, containerField);\n            il.Emit(OpCodes.Ret);\n            return containerField;\n        }\n\n        private static void DefineGenericParameters(MethodInfo targetMethod, MethodBuilder methodBuilder)\n        {\n            Type[] genericArguments = targetMethod.GetGenericArguments().ToArray();\n            GenericTypeParameterBuilder[] genericTypeParameters = methodBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray());\n            for (int i = 0; i < genericArguments.Length; i++)\n            {\n                genericTypeParameters[i].SetGenericParameterAttributes(genericArguments[i].GetTypeInfo().GenericParameterAttributes);\n                ApplyGenericConstraints(genericArguments[i], genericTypeParameters[i]);\n            }\n        }\n\n        private static void ApplyGenericConstraints(Type genericArgument, GenericTypeParameterBuilder genericTypeParameter)\n        {\n            var genericConstraints = genericArgument.GetTypeInfo().GetGenericParameterConstraints();\n            genericTypeParameter.SetInterfaceConstraints(genericConstraints.Where(gc => gc.GetTypeInfo().IsInterface).ToArray());\n            genericTypeParameter.SetBaseTypeConstraint(genericConstraints.FirstOrDefault(t => t.GetTypeInfo().IsClass));\n        }\n\n        private void ImplementMethods(TypeBuilder typeBuilder, Type interfaceType, FieldBuilder containerField)\n        {\n            var methods = interfaceType.GetTypeInfo().DeclaredMethods;\n            foreach (var method in methods)\n            {\n                ImplementMethod(typeBuilder, method, containerField);\n            }\n        }\n\n        private void ImplementMethod(TypeBuilder typeBuilder, MethodInfo method, FieldBuilder containerField)\n        {\n            var methodAttributes = method.Attributes ^ MethodAttributes.Abstract;\n            var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();\n            string serviceName = GetServiceName(method);\n\n            MethodBuilder methodBuilder = typeBuilder.DefineMethod(\n                                            method.Name,\n                                            methodAttributes,\n                                            method.ReturnType,\n                                            parameterTypes);\n\n            if (method.IsGenericMethod)\n            {\n                DefineGenericParameters(method, methodBuilder);\n            }\n\n            var il = methodBuilder.GetILGenerator();\n            il.Emit(OpCodes.Ldarg_0);\n            il.Emit(OpCodes.Ldfld, containerField);\n            for (int i = 0; i < parameterTypes.Length; i++)\n            {\n                il.Emit(OpCodes.Ldarg, i + 1);\n            }\n\n            MethodInfo closedGenericGetInstanceMethod;\n\n            if (string.IsNullOrEmpty(serviceName))\n            {\n                var openGenericGetInstanceMethod = GetInstanceMethods[parameterTypes.Length];\n                closedGenericGetInstanceMethod = openGenericGetInstanceMethod.MakeGenericMethod(parameterTypes.Concat(new[] { method.ReturnType }).ToArray());\n            }\n            else\n            {\n                var openGenericGetInstanceMethod = NamedGetInstanceMethods[parameterTypes.Length];\n                closedGenericGetInstanceMethod = openGenericGetInstanceMethod.MakeGenericMethod(parameterTypes.Concat(new[] { method.ReturnType }).ToArray());\n                il.Emit(OpCodes.Ldstr, serviceName);\n            }\n\n            il.Emit(OpCodes.Call, closedGenericGetInstanceMethod);\n            il.Emit(OpCodes.Ret);\n        }\n\n        private string GetServiceName(MethodInfo method)\n        {\n            return serviceNameResolver.Resolve(method);\n        }\n    }\n\n    /// <summary>\n    /// A class that is capable of creating a <see cref=\"TypeBuilder\"/> that\n    /// is used to build the proxy type.\n    /// </summary>\n    public class TypeBuilderFactory : ITypeBuilderFactory\n    {\n        /// <summary>\n        /// Creates a <see cref=\"TypeBuilder\"/> instance that is used to build a proxy\n        /// type that inherits/implements the <paramref name=\"targetType\"/> with an optional\n        /// set of <paramref name=\"additionalInterfaces\"/>.\n        /// </summary>\n        /// <param name=\"targetType\">The <see cref=\"Type\"/> that the <see cref=\"TypeBuilder\"/> will inherit/implement.</param>\n        /// <param name=\"additionalInterfaces\">A set of additional interfaces to be implemented.</param>\n        /// <returns>A <see cref=\"TypeBuilder\"/> instance for which to build the proxy type.</returns>\n        public TypeBuilder CreateTypeBuilder(Type targetType, Type[] additionalInterfaces)\n        {\n            ModuleBuilder moduleBuilder = GetModuleBuilder();\n            const TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.Class;\n            var typeName = targetType.Name + \"AutoFactory\";\n\n            Type[] interfaceTypes = new[] { targetType }.Concat(additionalInterfaces).ToArray();\n            var typeBuilder = moduleBuilder.DefineType(typeName, typeAttributes, null, interfaceTypes);\n            return typeBuilder;\n        }\n\n        /// <summary>\n        /// Creates a proxy <see cref=\"Type\"/> based on the given <paramref name=\"typeBuilder\"/>.\n        /// </summary>\n        /// <param name=\"typeBuilder\">The <see cref=\"TypeBuilder\"/> that represents the proxy type.</param>\n        /// <returns>The proxy <see cref=\"Type\"/>.</returns>\n        public Type CreateType(TypeBuilder typeBuilder)\n        {\n            return typeBuilder.CreateTypeInfo().AsType();\n        }\n\n        private static ModuleBuilder GetModuleBuilder()\n        {\n            AssemblyBuilder assemblyBuilder = GetAssemblyBuilder();\n            return assemblyBuilder.DefineDynamicModule(\"LightInject.AutoFactory.AutoFactoryAssembly\");\n        }\n\n        private static AssemblyBuilder GetAssemblyBuilder()\n        {\n            var assemblybuilder = AssemblyBuilder.DefineDynamicAssembly(\n            new AssemblyName(\"LightInject.AutoFactory.AutoFactoryAssembly\"), AssemblyBuilderAccess.Run);\n            return assemblybuilder;\n        }\n    }\n\n    /// <summary>\n    /// A class that is capable of resolving the\n    /// name of the service to be retrived based on a given method.\n    /// </summary>\n    public class ServiceNameResolver : IServiceNameResolver\n    {\n        /// <summary>\n        /// Resolves the name of the service to be retrieved based on the given <paramref name=\"method\"/>.\n        /// </summary>\n        /// <param name=\"method\">The method for which to resolve the service name.</param>\n        /// <returns>The name of the service if the method represent a named service, otherwise null.</returns>\n        public string Resolve(MethodInfo method)\n        {\n            var returnTypeName = ResolveReturnTypeName(method);\n            string serviceName = null;\n            var match = Regex.Match(method.Name, @\"Get(?!\" + returnTypeName + \")(.+)\");\n            if (match.Success)\n            {\n                serviceName = match.Groups[1].Captures[0].Value;\n            }\n\n            return serviceName;\n        }\n\n        private static string ResolveReturnTypeName(MethodInfo method)\n        {\n            var returnTypeName = GetFriendlyTypeName(method);\n\n            if (returnTypeName.StartsWith(\"I\"))\n            {\n                return returnTypeName.Substring(1);\n            }\n\n            return returnTypeName;\n        }\n\n        private static string GetFriendlyTypeName(MethodInfo method)\n        {\n            return Regex.Match(method.ReturnType.Name, @\"([a-zA-Z]+)\").Groups[1].Captures[0].Value;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Core.Interfaces/AssemblyInfo.cs",
    "content": "﻿// probably won't ever need this \n// [assembly: CompositionRootType(typeof(ServiceRegistrationCoreInterfaces))]"
  },
  {
    "path": "Diz.Core.Interfaces/CpuInterfaces.cs",
    "content": "﻿namespace Diz.Core.Interfaces;\n\n// wonder if we could ditch this one by using extension methods\npublic interface ISteppable\n{\n    int Step(int offset, bool branch, bool force, int prevOffset);\n}\n\npublic interface IAutoSteppable\n{\n    public int AutoStepSafe(int offset);\n    public int AutoStepHarsh(int offset, int count);\n}\n\npublic interface IMarkOperandAndOpcode\n{\n    public void MarkAsOpcodeAndOperandsStartingAt(\n        int offset, int? dataBank = null, int? directPage = null,\n        bool? xFlag = null, bool? mFlag = null\n        );\n}"
  },
  {
    "path": "Diz.Core.Interfaces/Diz.Core.Interfaces.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n        <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n        <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <Compile Remove=\"Interfaces.cs\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Core.Interfaces/Enums.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Drawing;\n\nnamespace Diz.Core.Interfaces;\n\n[AttributeUsage(AttributeTargets.All)]\npublic class ColorDescriptionAttribute(uint hexColor) : Attribute {\n\tpublic uint HexColor { get; } = hexColor;\n}\n\npublic enum FlagType : byte\n{\n\t[ColorDescription(0xD3D3D3)] // LightGray\n\tUnreached = 0x00,\n\n\t[ColorDescription(0xFFFF00)] // Yellow\n\tOpcode = 0x10,\n\n\t[ColorDescription(0x9ACD32)] // YellowGreen\n\tOperand = 0x11,\n\n\t[ColorDescription(0xFFDEAD)] [Description(\"Data (8-bit)\")] // NavajoWhite (base data color)\n\tData8Bit = 0x20,\n\n\t[ColorDescription(0xFFB6C1)] // LightPink\n\tGraphics = 0x21,\n\n\t[ColorDescription(0xB0E0E6)] // PowderBlue\n\tMusic = 0x22,\n\n\t[ColorDescription(0x2F4F4F)] // DarkSlateGray\n\tEmpty = 0x23,\n\n\t[ColorDescription(0xE6C898)] [Description(\"Data (16-bit)\")] // Slightly darker shade of Data base\n\tData16Bit = 0x30,\n\n\t[ColorDescription(0xDA70D6)] [Description(\"Pointer (16-bit)\")] // Orchid (base pointer color)\n\tPointer16Bit = 0x31,\n\n\t[ColorDescription(0xCDB285)] [Description(\"Data (24-bit)\")] // Darker shade of Data base\n\tData24Bit = 0x40,\n\n\t[ColorDescription(0xC55DC2)] [Description(\"Pointer (24-bit)\")] // Darker shade of Pointer base\n\tPointer24Bit = 0x41,\n\n\t[ColorDescription(0xB49C72)] [Description(\"Data (32-bit)\")] // Darkest shade of Data base\n\tData32Bit = 0x50,\n\n\t[ColorDescription(0xB04AAD)] [Description(\"Pointer (32-bit)\")] // Darkest shade of Pointer base\n\tPointer32Bit = 0x51,\n\n\t[ColorDescription(0x7FFFD4)] // Aquamarine\n\tText = 0x60\n}\n\npublic enum Architecture : byte\n{\n\t[Description(\"65C816\")] Cpu65C816 = 0x00,\n\t[Description(\"SPC700\")] Apuspc700 = 0x01,\n\t[Description(\"SuperFX\")] GpuSuperFx = 0x02\n}\n\n[Flags]\npublic enum InOutPoint : byte\n{\n\tNone = 0x00,\n\tInPoint = 0x01,\n\tOutPoint = 0x02,\n\tEndPoint = 0x04,\n\tReadPoint = 0x08\n}\n    \npublic enum RomSpeed : byte\n{\n\tSlowRom,\n\tFastRom,\n\tUnknown\n}\n    \npublic enum RomMapMode : byte\n{\n\tLoRom,\n\n\tHiRom,\n\n\tExHiRom,\n\n\t[Description(\"SA - 1 ROM\")] Sa1Rom,\n\n\t[Description(\"SA-1 ROM (FuSoYa's 8MB mapper)\")]\n\tExSa1Rom,\n\n\tSuperFx,\n\n\t[Description(\"Super MMC\")] SuperMmc,\n\n\tExLoRom\n}\n\t\npublic enum ColumnType : int\n{\n\tLabel,\n\tOffset,\n\tAsciiCharRep,\n\tNumericRep,\n\tPoint,\n\tInstruction,\n\tIA,\n\tTypeFlag,\n\tDataBank,\n\tDirectPage,\n\tMFlag,\n\tXFlag,\n\tComment\n}"
  },
  {
    "path": "Diz.Core.Interfaces/LabelInterfaces.cs",
    "content": "﻿using System.Collections.ObjectModel;\nusing System.ComponentModel;\n\nnamespace Diz.Core.Interfaces;\n\npublic interface IRegion : INotifyPropertyChanged\n{\n    int StartSnesAddress { get; set; }\n    int EndSnesAddress { get; set; }\n    \n    // Must be unique in this project\n    string RegionName { get; set; }\n    \n    // ------------------------------------------------------------\n    // region effects (if these get more complex, split them out)\n    // ------------------------------------------------------------\n    \n    // labels inside this region should look for an alternative label context to apply\n    // if a label has a context that matches this name, it will use THAT as the label name,\n    // instead of its normal name.\n    // i.e. if we define a region called BATTLE with context \"Battle\", and a label called \"tmp50\" is within this region\n    // and matches the context name \"Battle\", it'll use the its alternate label name (say, \"player_hp\") here.\n    // this is super-useful to help deal with different parts of the game re-using the same address for different things\n    // i.e. menu vs battle vs overworld all using RAM address 0x50 for different stuff depending on which mode the game is in.\n    string ContextToApply { get; set; }\n    \n    // higher number = higher priority in case of overlapping regions\n    int Priority { get; set; } \n    \n    // if true, when exporting assembly, this region will go into a separate file.\n    // overlapping regions will either be disallowed for this, or go in priority order.\n    bool ExportSeparateFile { get; set; }\n}\n\npublic interface IReadOnlyContextMapping : INotifyPropertyChanged\n{\n    string Context { get; }\n    string NameOverride  { get; }\n}\n\npublic interface IContextMapping : IReadOnlyContextMapping\n{\n    new string Context { get; set;  }\n    new string NameOverride  { get; set; }\n}\n\n\npublic interface IReadOnlyLabel\n{\n    string Name { get; }\n    string Comment { get; }\n    IEnumerable<IReadOnlyContextMapping> ContextMappings { get; }\n\n}\n\npublic interface IAnnotationLabel : IReadOnlyLabel\n{\n    // name used for default context\n    new string Name { get; set; }\n    new string Comment { get; set; }\n    \n    // label names can change based on which \"context\" they're in\n    // by default, this is empty but can be overridden\n    new ObservableCollection<IContextMapping> ContextMappings { get; }\n    \n    // get a label name using a specific context, if it exists. otherwise return the default name\n    string GetName(string contextName = \"\");\n}\n    \npublic interface IReadOnlyLabelProvider\n{\n    public IEnumerable<KeyValuePair<int, IAnnotationLabel>> Labels { get; }\n\n    IAnnotationLabel? GetLabel(int snesAddress);\n    string? GetLabelName(int snesAddress);\n    string GetLabelComment(int snesAddress);\n    \n    // optimization: optional: get a provider that can give a smaller subset\n    // of labels for assembly logging output, to reduce search space and improve export speed.\n    public IMirroredLabelCacheSearch? MirroredLabelCacheSearch { get; }\n}\n\npublic interface IMirroredLabelCacheSearch\n{\n    (int labelAddress, IAnnotationLabel? labelEntry) SearchOptimizedForMirroredLabel(int snesAddress);\n}\n\npublic interface ILabelProvider : IReadOnlyLabelProvider\n{\n    void AddLabel(int snesAddress, IAnnotationLabel label, bool overwrite = false);\n    void DeleteAllLabels();\n        \n    // if any labels exist at this address, remove them\n    void RemoveLabel(int snesAddress);\n    \n    void SetAll(Dictionary<int, IAnnotationLabel> newLabels);\n    void AppendLabels(Dictionary<int, IAnnotationLabel> newLabels, bool smartMerge = false);\n    \n    void SortLabels();\n}\n\npublic interface IReadOnlyLabels\n{\n    public IReadOnlyLabelProvider Labels { get; }\n}"
  },
  {
    "path": "Diz.Core.Interfaces/ModelInterfaces.cs",
    "content": "﻿using System.Collections.Specialized;\nusing System.ComponentModel;\n\n#if DIZ_3_BRANCH\nusing Diz.Core.model.byteSources;\n#endif\n\nnamespace Diz.Core.Interfaces\n{\n    public interface IReadOnlyByteSource\n    {\n        byte? GetRomByte(int offset);\n        int? GetRomWord(int offset);\n        int? GetRomLong(int offset);\n        int? GetRomDoubleWord(int offset);\n    }\n\n    public interface IInOutPointSettable\n    {\n        public void SetInOutPoint(int i, InOutPoint point);\n        public void ClearInOutPoint(int i);\n        \n        void RescanInOutPoints();\n    }\n    \n    public interface IInOutPointGettable\n    {\n        InOutPoint GetInOutPoint(int offset);   \n    }\n\n    public interface IRomByteFlagsGettable\n    {\n        int GetMxFlags(int i);\n        bool GetMFlag(int i);\n        bool GetXFlag(int i);\n\n        int GetDataBank(int offset);\n        int GetDirectPage(int offset);\n        FlagType GetFlag(int offset);\n    }\n\n    public interface IRomByteFlagsSettable\n    {\n        void SetMxFlags(int i, int mx);\n        void SetMFlag(int romOffset, bool value);\n        void SetXFlag(int romOffset, bool value);\n\n        void SetDataBank(int romOffset, int result);\n        void SetDirectPage(int romOffset, int result);\n        void SetFlag(int offset, FlagType flagType);\n    }\n\n    public interface IArchitectureGettable \n    {\n        public Architecture GetArchitecture(int i);\n    }\n    \n    public interface IArchitectureSettable \n    {\n        public void SetArchitecture(int i, Architecture arch);\n    }\n\n    public interface IRomMapProvider\n    {\n        // eventually, remove the 'set' if we can just to make this read-only\n        RomMapMode RomMapMode { get; set; }\n        RomSpeed RomSpeed { get; set; }\n    }\n\n    public interface IRomSize\n    {\n        int GetRomSize();\n        int GetBankSize();\n    }\n\n    public interface ISnesBankInfo\n    {\n        int GetNumberOfBanks();\n        string GetBankName(int bankIndex);\n    }\n    \n    public class CpuInstructionDataFormatted\n    {\n        // the final text that should be printed\n        // (most of the time, this is the main thing you're after)\n        public string FullGeneratedText { get; set; } = \"\";\n        \n        // stuff below is the info that went into generating the above fully generated text\n        // useful if you need more context. feel free to expose/save more into this struct as you need it\n        \n        // if the operand was overridden, what was the overridden text?\n        public string OverriddenOperand1 { get; set; } = \"\";\n        public string OverriddenOperand2 { get; set; } = \"\";\n        \n        // if the operand was overridden, what was the NON-overridden original value?\n        public string OriginalNonOverridenOperand1 { get; set; } = \"\";\n        public string OriginalNonOverridenOperand2 { get; set; } = \"\";\n    }\n    \n    public interface IInstructionGettable\n    {\n        int GetInstructionLength(int offset);\n        string GetInstructionStr(int offset);\n        public CpuInstructionDataFormatted GetInstructionData(int offset);\n    }\n    \n    public interface IMarkable\n    {\n        int Mark(Action<int> markAction, int offset, int count);\n    }\n\n    public interface IRomByteBase\n    {\n        byte Rom { get; set; }\n        int Offset { get; }\n    }\n\n    public interface ISnesRomByte : INotifyPropertyChanged\n    {\n        byte DataBank { get; set; }\n        int DirectPage { get; set;}\n        bool XFlag { get; set;}\n        bool MFlag { get; set;}\n        FlagType TypeFlag { get; set; }\n        Architecture Arch { get; set; }\n        InOutPoint Point { get; set; }\n    }\n    \n    public interface IRomByte : \n        IRomByteBase, \n        ISnesRomByte // eventually, need to get the ISnesRomByte out of here.\n    {\n        public ReaderWriterLockSlim Lock { get; }\n        public bool EqualsButNoRomByte(IRomByte other);\n    }\n\n    public interface IRomBytes<out T> : IEnumerable<T>, INotifyCollectionChanged, INotifyPropertyChanged \n        where T : IRomByte \n    {\n        void Clear();\n        \n        public IRomByte this[int i] { get; }\n        \n        int Count { get; }\n        bool SendNotificationChangedEvents { get; set; }\n        void SetBytesFrom(IReadOnlyList<byte> copyFrom, int dstStartingOffset);\n    }\n    \n    public interface IRomBytesProvider\n    {\n        public IRomBytes<IRomByte> RomBytes { get; }\n    }\n\n    public static class RomBytesProviderExtension\n    {\n        // expensive and inefficient helper, don't use if you care about perf\n        public static List<byte> CreateListRawRomBytes(this IEnumerable<IRomByte> romBytes) =>\n            romBytes.Select(x => x.Rom).ToList();\n    }\n    \n    \n\n    // would love to redesign so we can get rid of this class and all this temporary label stuff.\n    public interface ITemporaryLabelProvider : ILabelProvider\n    {\n        // add a temporary label which will be cleared out when we are finished the export\n        // this should not add a label if a real label already exists.\n        public void AddOrReplaceTemporaryLabel(int snesAddress, IAnnotationLabel label);\n        public void ClearTemporaryLabels();\n        void LockLabelsCache();\n        void UnlockLabelsCache();\n    }\n\n    public interface ILabelService : \n        ILabelProvider,\n        IReadOnlyLabelProvider\n    {\n    }\n    \n    public interface ILabelServiceWithTempLabels : \n        ILabelService,\n        ITemporaryLabelProvider\n    {\n        \n    }\n// TODO: below: #if DIZ_3_BRANCH\n//         ,ICommentProvider,\n//         IAnnotationProvider,\n//         IByteGraphProvider\n// #endif\n    \n    public interface IData : \n        INotifyPropertyChanged,\n        IReadOnlyByteSource,\n        IRomMapProvider,\n        IRomBytesProvider,\n        IMarkable,\n        IArchitectureSettable,\n        IArchitectureGettable,\n        ICommentTextProvider,\n        IRegionProvider\n    {\n        IDataStoreProvider<IArchitectureApi> Apis { get; }\n        IDataStoreProvider<IDataTag> Tags { get; }\n\n        ILabelServiceWithTempLabels Labels { get; }\n        \n        public SortedDictionary<int, string> Comments { get; }\n    }\n\n    public static class DataExtensions\n    {\n        public static T? GetApi<T>(this IData @this) where T : class, IArchitectureApi => \n            @this.Apis.Get<T>();\n        public static T? GetTag<T>(this IData @this) where T : class, IDataTag => \n            @this.Tags.Get<T>();\n    }\n    \n    public interface ISnesCachedVerificationInfo\n    {\n        public string InternalRomGameName { get; set; }\n        public uint InternalCheckSum { get; set; }\n    }\n    \n    public interface IDataStoreProvider<T> : IEnumerable<T> where T : class\n    {\n        bool AddIfDoesntExist(T type);\n        TSearchFor Get<TSearchFor>() where TSearchFor : class, T;\n    }\n    \n    // // API for a specific architecture API (like SNES, genesis, etc)\n    public interface IArchitectureApi\n    {\n        \n    }\n    \n    public interface IDataTag\n    {\n        \n    }\n\n    public interface ISnesIntermediateAddress\n    {\n        int GetIntermediateAddressOrPointer(int offset);\n        \n        // -1 if not found\n        int GetIntermediateAddress(int offset, bool resolve = false);\n        \n        bool IsMatchingIntermediateAddress(int intermediateAddress, int addressToMatch);\n    }\n    \n    public interface ISnesAddressConverter\n    {\n        int ConvertPCtoSnes(int offset);\n        int ConvertSnesToPc(int offset);\n    }\n}"
  },
  {
    "path": "Diz.Core.Interfaces/OtherInterfaces.cs",
    "content": "﻿using System.Collections.ObjectModel;\n\nnamespace Diz.Core.Interfaces;\n\npublic interface IRegionProvider\n{\n    ObservableCollection<IRegion> Regions { get; }\n    IRegion? GetRegion(int snesAddress);\n\n    // create a new region (doesn't add it to collection)\n    IRegion? CreateNewRegion();\n}\n\npublic interface ICommentTextProvider\n{\n    // search both ROM comments and applicable label comments\n    string GetCommentText(int snesAddress);\n    \n    // search just ROM comments\n    string? GetComment(int snesAddress);\n}\n\n#if DIZ_3_BRANCH\n    public interface IAnnotationProvider\n    {\n        public T GetOneAnnotationAtPc<T>(int pcOffset) where T : Annotation, new();   \n}\n\n    public interface IByteGraphProvider\n    {\n        ByteEntry BuildFlatByteEntryForSnes(int snesAddress);\n        ByteEntry BuildFlatByteEntryForRom(int snesAddress);\n    }\n#endif\n\n// utility for getting info about the running app\npublic interface IAppVersionInfo\n{\n    enum AppVersionInfoType\n    {\n        Version,\n        FullDescription,\n    }\n    \n    string GetVersionInfo(AppVersionInfoType type);\n}"
  },
  {
    "path": "Diz.Core.Interfaces/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {}\n  }\n}"
  },
  {
    "path": "Diz.Cpu.65816/Diz.Cpu.65816.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n        <RootNamespace>Diz.Cpu._65816</RootNamespace>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n        <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\Diz.Core.Interfaces\\Diz.Core.Interfaces.csproj\" />\n      <ProjectReference Include=\"..\\Diz.Core\\Diz.Core.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Cpu.65816/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"LightInject\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"FluentValidation\": \"[11.9.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"JetBrains.Profiler.SelfApi\": \"[2.5.0, )\",\n          \"LightInject\": \"[6.6.4, )\",\n          \"SharpZipLib\": \"[1.4.2, )\",\n          \"System.Diagnostics.Tracing\": \"[4.3.0, )\",\n          \"System.Resources.Extensions\": \"[8.0.0, )\",\n          \"System.Text.Encoding.CodePages\": \"[8.0.0, )\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/AddRomDataCommand.cs",
    "content": "﻿using Diz.Core.model;\nusing Diz.Core.model.project;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing FluentValidation;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816;\n\n/// <summary>\n/// After a project file is deserialized from XML, it contains all its info EXCEPT\n/// we don't store the bytes of the actual ROM file in the project file (for copyright and redundancy reasons).\n/// So, on post-serialization we need to add the bytes from the ROM file on disk into our newly deserialized Project class\n/// We also need to verify that we have the right file by running checks (like rom title and checksum) that we\n/// indeed have the right file on disk/etc.\n///\n/// This class is designed to open a ROM file on disk and safely copy it into a Project file, ensuring data integrity.\n/// </summary>\n[UsedImplicitly]\npublic class AddRomDataCommand : IAddRomDataCommand\n{\n    public AddRomDataCommand(Func<ILinkedRomBytesProvider> createLinkedProvider)\n    {\n        this.createLinkedProvider = createLinkedProvider;\n    }\n\n    public bool ShouldProjectCartTitleMatchRomBytes { get; set; } = true;\n    public ProjectXmlSerializer.Root? Root { get; set; } = null;\n    public Func<string, string>? GetNextRomFileToTry { get; set; }\n    public IMigrationRunner? MigrationRunner { get; set; }\n\n    private Project? Project => Root?.Project ?? null;\n\n    public void TryReadAttachedProjectRom()\n    {\n        if (Root?.Project == null)\n            throw new InvalidDataException(\"Root element should contain a Project element, but none was found.\");\n\n        MigrationRunner?.OnLoadingBeforeAddLinkedRom(this);\n        Populate();\n        MigrationRunner?.OnLoadingAfterAddLinkedRom(this);\n    }\n\n    private void Populate()\n    {\n        // for copyright reasons, normally, we don't store the actual bytes from the ROM in the XML directly.\n        // we only save metadata about them, and we populate them from the ROM file on disk as the last step\n        // after the project is finished loading.\n        //\n        // However, different project loaders or generators may choose to do this (such as the sample data generator,\n        // or, for test roms). So, don't try and load anything from a ROM file in disk if something else already\n        // populated the bytes in the project.\n        if (Project?.Data?.RomBytesLoaded ?? false)\n            return;\n\n        // Normal case: find a ROM file on disk matching our \n        FillIfSearchFoundRom();\n    }\n\n    private void FillIfSearchFoundRom()\n    {\n        if (Project == null)\n            throw new InvalidOperationException(\"Project not allowed to be null here\");\n        \n        var romFileData = SearchForValidRom();\n        if (romFileData == null)\n            throw new InvalidOperationException(\"Search failed, couldn't find compatible ROM to link\");\n\n        var (filename, romBytes) = romFileData.Value;\n        \n        Project.AttachedRomFilename = filename;\n        Project.Data.RomBytes.CopyRomDataIn(romBytes);\n    }\n\n    private readonly Func<ILinkedRomBytesProvider> createLinkedProvider;\n\n    private (string filename, byte[] romBytes)? SearchForValidRom()\n    {\n        var searchProvider = createLinkedProvider();\n        searchProvider.EnsureCompatible = (romFilename, romBytes) => EnsureProjectCompatibleWithRom(romBytes);\n        searchProvider.GetNextFilename = reasonWhyLastFileNotCompatible =>\n            GetNextRomFileToTry?.Invoke(reasonWhyLastFileNotCompatible) ?? null;\n\n        // some other hints for the user for what ROM they should be looking for\n        var extraPromptText = $\"{Project?.InternalRomGameName ?? \"\"}\";\n            \n        return searchProvider.SearchAndReadFromCompatibleRom(\n            initialRomFile: Project?.AttachedRomFilename ?? \"\",\n            extraPromptText\n            );\n    }\n\n    private void EnsureProjectCompatibleWithRom(byte[] romFileBytes)\n    {\n        var container = new RomToProjectAssociation\n        {\n            Project = Project,\n            RomBytes = romFileBytes,\n        };\n        var validator = CreateValidator();\n        var results = validator.Validate(container);\n        if (!results.IsValid)\n            throw new InvalidDataException(results.ToString());\n    }\n\n    private IValidator<RomToProjectAssociation> CreateValidator() =>\n        CreateValidator(ShouldProjectCartTitleMatchRomBytes);\n\n    private static IValidator<RomToProjectAssociation> CreateValidator(bool shouldProjectCartTitleMatchRomBytes)\n    {\n        var validator = new AddRomDataCommandValidator\n        {\n            EnsureProjectAndRomCartTitleMatch = shouldProjectCartTitleMatchRomBytes\n        };\n        return validator;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/AddRomDataCommandValidator.cs",
    "content": "﻿#nullable enable\n\nusing Diz.Core.model;\nusing Diz.Core.util;\nusing FluentValidation;\n\nnamespace Diz.Cpu._65816;\n\npublic class RomToProjectAssociation\n{\n    public byte[]? RomBytes;\n    public Project? Project;\n}\n    \npublic class AddRomDataCommandValidator : AbstractValidator<RomToProjectAssociation>\n{\n    public bool EnsureProjectAndRomCartTitleMatch { get; set; } = true;\n\n    public AddRomDataCommandValidator()\n    {\n        RuleFor(x => x.Project).NotEmpty();\n        RuleFor(x => x.Project!.Data).NotEmpty();\n        RuleFor(x => x.RomBytes).NotEmpty();\n        RuleFor(x => x.Project).Must(HaveValidRomSize);\n        RuleFor(x => x.Project).Must(MatchChecksums);\n        RuleFor(x => x.Project).Must(MatchCartTitle);\n    }\n        \n    private static bool MatchChecksums(RomToProjectAssociation container, Project? project,\n        ValidationContext<RomToProjectAssociation> validationContext)\n    {\n        if (project == null) \n            throw new ArgumentNullException(nameof(project));\n\n        var snesData = project.Data.GetSnesApi() ?? throw new ArgumentNullException();\n        var checksumToVerify =\n            ByteUtil.ConvertByteArrayToUInt32(container.RomBytes, snesData.RomComplementOffset);\n            \n        if (checksumToVerify == project.InternalCheckSum)\n            return true;\n\n        validationContext.AddFailure($\"Expected checksum: {project.InternalCheckSum:X8}'. found: '{checksumToVerify:X8}'\");\n        return false;\n    }\n\n    private static bool HaveValidRomSize(RomToProjectAssociation container, Project? project, ValidationContext<RomToProjectAssociation> validationContext)\n    {\n        var snesData = project?.Data.GetSnesApi();\n        if (snesData == null)\n            return false;\n        \n        if (container.RomBytes?.Length > snesData.RomSettingsOffset + 10)\n            return true;\n\n        validationContext.AddFailure(\"The linked ROM is too small. It can't be opened.\");\n        return false;\n    }\n\n    private bool MatchCartTitle(RomToProjectAssociation container, Project? project,\n        ValidationContext<RomToProjectAssociation> validationContext)\n    {\n        if (!EnsureProjectAndRomCartTitleMatch)\n            return true;\n            \n        var snesData = project?.Data.GetSnesApi();\n        if (project == null || snesData == null)\n            return false;\n            \n        var gameNameFromRomBytes = RomUtil.GetCartridgeTitleFromRom(container.RomBytes, snesData.RomSettingsOffset);\n        var requiredGameNameMatch = project.InternalRomGameName;\n\n        return MatchCartTitle(requiredGameNameMatch, gameNameFromRomBytes, validationContext);\n    }\n\n    private static bool MatchCartTitle(string requiredGameNameMatch, string gameNameFromRomBytes,\n        ValidationContext<RomToProjectAssociation> validationContext)\n    {\n        if (requiredGameNameMatch == gameNameFromRomBytes)\n            return true;\n\n        // do not rely on this string cleanup for anything important.\n        // it's just trying to kill any non-ascii chars so we can print the message successfully.\n        // real-life encodings can be weird here\n        var cleanName = new string(gameNameFromRomBytes.Where(c => c >= 32 && c <= 126).ToArray());\n\n        validationContext.AddFailure(\n            $\"ROM header title name mismatch. Expected: '{requiredGameNameMatch}', Actual: '{cleanName}'.\");\n        return false;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/AssemblyInfo.cs",
    "content": "﻿using Diz.Cpu._65816;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizCpu65816ServiceRoot))]\n\n"
  },
  {
    "path": "Diz.Cpu.65816/src/AutoStepper65816.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model;\n\nnamespace Diz.Cpu._65816;\n\npublic class AutoStepper65816<TDataSource> where TDataSource : IRomByteFlagsGettable, IRomByteFlagsSettable, IReadOnlyByteSource, ISteppable\n{\n    public int Offset { get; private set; }\n    private int prevOffset = -1;\n\n    private readonly TDataSource byteSource;\n    \n    private readonly List<int> seenBranches;\n    private readonly Stack<int> stack;\n\n    public AutoStepper65816(TDataSource byteSource)\n    {\n        this.byteSource = byteSource;\n        \n        stack = new Stack<int>();\n        seenBranches = new List<int>();\n    }\n\n    public void Run(int offset)\n    {\n        Offset = offset;\n        prevOffset = offset - 1;\n\n        while (ProcessAutoStepOne() && CanAutoStepAtCurrentOffset()) {}\n    }\n\n    private bool CanAutoStepAtCurrentOffset() => \n        byteSource.GetFlag(Offset) is FlagType.Unreached or FlagType.Opcode or FlagType.Operand;\n    \n    /// <summary>\n    /// Complete one step in Safe AutoStep.\n    /// modifies internal state and sets Offset to the next state required\n    /// </summary>\n    /// <returns>true if we should continue autostepping</returns>\n    private bool ProcessAutoStepOne()\n    {\n        if (seenBranches.Contains(Offset))\n            return false;\n\n        var romByte = byteSource.GetRomByte(Offset);\n        if (romByte == null)\n            return false;\n        \n        var opcode = (byte)romByte;\n        \n        RememberIfBranch(opcode);\n        \n        if (!HandleOpcode(opcode))\n            return false;\n\n        var shouldContinue =\n            opcode is not (\n                0x40 or 0xCB or 0xDB or 0xF8 or // RTI WAI STP SED\n                0xFB or 0x00 or 0x02 or 0x42 or // XCE BRK COP WDM\n                0x6C or 0x7C or 0xDC or 0xFC    // JMP JMP JML JSR\n                );\n\n        return shouldContinue;\n    }\n\n    private (int nextOffsetIfNoJump, int nextOffsetIfJumped) StepAndMarkAllBranchesFromOffset()\n    {\n        // from our current offset, step (marks things) and get the offset for what happens if we're:\n        // 1. at a branch and we take it\n        // 2. at a branch and we don't take it\n        // if we're not at a branch, this just grabs the next instruction normally\n        \n        // note: we don't actually modify Offset here to follow the jump. we're just using it to:\n        // 1. peek ahead and see where we'd land if we did take vs not take the branch \n        // 2. mark the next instructions on either side of the branch as opcodes/operands\n        \n        return (\n            nextOffsetIfNoJump: byteSource.Step(Offset, false, false, prevOffset),\n            nextOffsetIfJumped: byteSource.Step(Offset, true, false, prevOffset)\n        );\n    }\n\n    private bool HandleOpcode(byte opcode)\n    {\n        var (nextOffsetIfNoJump, nextOffsetIfJumped) = StepAndMarkAllBranchesFromOffset();\n\n        if (!PushOrPopMxFlags(opcode)) \n            return false;\n\n        switch (opcode) \n        {\n            // RTS RTL\n            case 0x60: case 0x6B:\n                if (stack.Count == 0)\n                    return false;\n\n                AdvanceOffsetTo(stack.Pop());\n                return true;\n\n            // JSR JSL\n            case 0x20: case 0x22:\n                stack.Push(nextOffsetIfNoJump);\n                AdvanceOffsetTo(nextOffsetIfJumped);\n                return true;\n\n            default:\n                AdvanceOffsetTo(nextOffsetIfNoJump);\n                return true;\n        }\n    }\n\n    private void AdvanceOffsetTo(int offset)\n    {\n        prevOffset = Offset;\n        Offset = offset;\n    }\n\n    private bool PushOrPopMxFlags(byte opcode)\n    {\n        if (opcode == 0x08)\n        {\n            // PHP\n            stack.Push(byteSource.GetMxFlags(Offset));\n        } \n        else if (opcode == 0x28)\n        {\n            // PLP\n            if (stack.Count == 0)\n                return false;\n\n            byteSource.SetMxFlags(Offset, stack.Pop());\n        }\n\n        return true;\n    }\n\n    private void RememberIfBranch(byte opcode)\n    {\n        var opcodeIsBranch =\n            opcode is\n                0x4C or 0x5C or 0x80 or 0x82 or // JMP JML BRA BRL\n                0x10 or 0x30 or 0x50 or 0x70 or // BPL BMI BVC BVS\n                0x90 or 0xB0 or 0xD0 or 0xF0; // BCC BCS BNE BEQ\n\n        if (opcodeIsBranch)\n            seenBranches.Add(Offset);\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/CPU65C816.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\n\nnamespace Diz.Cpu._65816;\n\npublic class Cpu65C816<TByteSource> : Cpu<TByteSource> \n    where TByteSource : \n    IRomByteFlagsGettable, \n    IRomSize,\n    IRomByteFlagsSettable, \n    ISnesAddressConverter, \n    ISteppable, \n    IReadOnlyByteSource, \n    ISnesIntermediateAddress,\n    IInOutPointSettable,\n    IInOutPointGettable,\n    IReadOnlyLabels,\n    ICommentTextProvider,\n    IRegionProvider\n{\n    // TODO: expose these somehow to the project settings\n    public bool AttemptTouseDirectPageArithmeticInFinalOutput { get; set; } = true;\n    public bool AttemptToUnmirrorLabels { get; set; } = true;\n    \n    public override int Step(TByteSource data, int offset, bool branch, bool force, int prevOffset = -1)\n    {\n        var (opcode, directPage, dataBank, xFlag, mFlag) = data.GetCpuStateFor(offset, prevOffset);\n        var length = MarkAsOpcodeAndOperandsStartingAt(data, offset, dataBank, directPage, xFlag, mFlag);\n        MarkInOutPoints(data, offset);\n\n        var nextOffset = offset + length;\n\n        var useIndirectAddress = \n            opcode is 0x4C or 0x5C or 0x80 or 0x82 || \n            branch && opcode is 0x10 or 0x30 or 0x50 or 0x70 or 0x90 or 0xB0 or 0xD0 or 0xF0 or 0x20 or 0x22;\n\n        if (force || !useIndirectAddress) \n            return nextOffset;\n            \n        var iaNextOffsetPc = data.ConvertSnesToPc(GetIntermediateAddress(data, offset, true));\n        if (iaNextOffsetPc >= 0)\n            return iaNextOffsetPc;\n\n        return nextOffset;\n    }\n    \n    public override int CalculateInOutPointsFromOffset(\n        TByteSource data,\n        int offset,\n        out InOutPoint newIaInOutPoint,\n        out InOutPoint newOffsetInOutPoint\n    )\n    {\n        // calculate these from scratch (don't rely on existing in-out data)\n        newIaInOutPoint = InOutPoint.None;\n        newOffsetInOutPoint = InOutPoint.None;\n\n        var opcode = data.GetRomByte(offset);\n        if (opcode == null)\n            return -1;\n\n        var intermediateAddress = data.GetIntermediateAddress(offset, true);\n        var iaOffsetPc = data.ConvertSnesToPc(intermediateAddress);\n\n        // set read point on EA\n        if (iaOffsetPc >= 0 && ( // these are all read/write/math instructions\n                ((opcode & 0x04) != 0) || ((opcode & 0x0F) == 0x01) || ((opcode & 0x0F) == 0x03) ||\n                ((opcode & 0x1F) == 0x12) || ((opcode & 0x1F) == 0x19)) &&\n            (opcode != 0x45) && (opcode != 0x55) && (opcode != 0xF5) && (opcode != 0x4C) &&\n            (opcode != 0x5C) && (opcode != 0x6C) && (opcode != 0x7C) && (opcode != 0xDC) && (opcode != 0xFC)\n           )\n        {\n            newIaInOutPoint |= InOutPoint.ReadPoint;\n        }\n\n        // set end point on offset\n        if (opcode == 0x40 || opcode == 0x4C || opcode == 0x5C || opcode == 0x60 // RTI JMP JML RTS\n            || opcode == 0x6B || opcode == 0x6C || opcode == 0x7C || opcode == 0x80 // RTL JMP JMP BRA\n            || opcode == 0x82 || opcode == 0xDB || opcode == 0xDC // BRL STP JML\n           )\n        {\n            newOffsetInOutPoint |= InOutPoint.EndPoint;\n        }\n\n        // set out point on offset\n        // set in point on EA\n        if (iaOffsetPc >= 0 && (\n                opcode == 0x4C || opcode == 0x5C || opcode == 0x80 || opcode == 0x82 // JMP JML BRA BRL\n                || opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 // BPL BMI BVC BVS\n                || opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0 // BCC BCS BNE BEQ\n                || opcode == 0x20 || opcode == 0x22)) // JSR JSL\n        {\n            newOffsetInOutPoint |= InOutPoint.OutPoint;\n            newIaInOutPoint |= InOutPoint.InPoint;\n        }\n\n        return iaOffsetPc;\n    }\n\n    public int MarkAsOpcodeAndOperandsStartingAt(\n        TByteSource data, int offsetToMarkAsOpcode, \n        int? dataBank = null, int? directPage = null, \n        bool? xFlag = null, bool? mFlag = null              // you pretty much always want to set the MX flags or this function is worthless\n        )\n    {\n        var numBytesToChangeForOpcodesAndOperands = 1;\n        var i = 0;\n        var markedAs = FlagType.Opcode;\n        do\n        {\n            var currentOffset = offsetToMarkAsOpcode + i;\n\n            if (dataBank != null)\n                data.SetDataBank(currentOffset, dataBank.Value);\n            \n            if (directPage != null)\n                data.SetDirectPage(currentOffset, directPage.Value);\n            \n            if (xFlag != null)\n                data.SetXFlag(currentOffset, xFlag.Value);\n            \n            if (mFlag != null)\n                data.SetMFlag(currentOffset, mFlag.Value);\n            \n            data.SetFlag(currentOffset, markedAs);\n            \n            if (markedAs == FlagType.Opcode)\n            {\n                // call GetInstructionLength() only AFTER setting all the other flags above,\n                // because the data for the instruction will CHANGE based on those flags.\n                // we want to get that, and apply it to the next couple operands\n                numBytesToChangeForOpcodesAndOperands = GetInstructionLength(data, offsetToMarkAsOpcode);\n            }\n\n            markedAs = FlagType.Operand;\n            ++i;\n        } while (i < numBytesToChangeForOpcodesAndOperands);\n\n        return numBytesToChangeForOpcodesAndOperands;\n    }\n\n    // input: ROM offset\n    // return: a SNES address\n    public override int GetIntermediateAddress(TByteSource data, int offset, bool resolve)\n    {\n        int bank;\n        int programCounter;\n            \n        #if !DIZ_3_BRANCH\n        // old way\n        var opcode = data.GetRomByte(offset);\n        #else\n            // new way\n            var byteEntry = GetByteEntryRom(data, offset);\n            var opcode = byteEntry?.Byte;\n        #endif\n            \n        if (opcode == null)\n            return -1;\n\n        var mode = GetAddressMode(data, offset);\n        switch (mode)\n        {\n            case Cpu65C816Constants.AddressMode.DirectPage:\n            case Cpu65C816Constants.AddressMode.DirectPageXIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageXIndexIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirectYIndex:\n                if (resolve)\n                {\n                    var directPage = data.GetDirectPage(offset);\n                    var operand = data.GetRomByte(offset + 1);\n                    if (!operand.HasValue)\n                        return -1;\n                    return (directPage + (int)operand) & 0xFFFF;\n                }\n                else\n                {\n                    goto case Cpu65C816Constants.AddressMode.DirectPageSIndex;\n                }\n            case Cpu65C816Constants.AddressMode.DirectPageSIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageSIndexIndirectYIndex:\n                return data.GetRomByte(offset + 1) ?? -1;\n            case Cpu65C816Constants.AddressMode.Address:\n            case Cpu65C816Constants.AddressMode.AddressXIndex:\n            case Cpu65C816Constants.AddressMode.AddressYIndex:\n            case Cpu65C816Constants.AddressMode.AddressXIndexIndirect:\n            {\n                bank = opcode is 0x20 or 0x4C or 0x7C or 0xFC\n                    ? data.ConvertPCtoSnes(offset) >> 16\n                    : data.GetDataBank(offset);\n                var operand = data.GetRomWord(offset + 1);\n                if (!operand.HasValue)\n                    return -1;\n                    \n                return (bank << 16) | (int)operand;\n            }\n            case Cpu65C816Constants.AddressMode.AddressIndirect:\n            case Cpu65C816Constants.AddressMode.AddressLongIndirect:\n            {\n                var operand = data.GetRomWord(offset + 1) ?? -1;\n                return operand;\n            }\n            case Cpu65C816Constants.AddressMode.Long:\n            case Cpu65C816Constants.AddressMode.LongXIndex:\n            {\n                var operand = data.GetRomLong(offset + 1) ?? -1;\n                return operand;\n            }\n            case Cpu65C816Constants.AddressMode.Relative8:\n            {\n                programCounter = data.ConvertPCtoSnes(offset + 2);\n                bank = programCounter >> 16;\n                var romByte = data.GetRomByte(offset + 1);\n                if (!romByte.HasValue)\n                    return -1;\n                    \n                return (bank << 16) | ((programCounter + (sbyte)romByte) & 0xFFFF);\n            }\n            case Cpu65C816Constants.AddressMode.Relative16:\n            {\n                // something may be wrong here with the \"PER\" instruction (opcode 0x62).\n                // description in https://github.com/IsoFrieze/DiztinGUIsh/issues/102\n                // must fix\n                \n                programCounter = data.ConvertPCtoSnes(offset + 3);\n                bank = programCounter >> 16;\n                var romByte = data.GetRomWord(offset + 1);\n                if (!romByte.HasValue)\n                    return -1;\n                    \n                return (bank << 16) | ((programCounter + (short)romByte) & 0xFFFF);\n            }\n        }\n        return -1;\n    }\n    \n    public override string GetInstructionStr(TByteSource data, int offset) => \n        GetInstructionData(data, offset).FullGeneratedText; // shortcut\n\n    public override CpuInstructionDataFormatted GetInstructionData(TByteSource data, int offset)\n    {\n        var mode = GetAddressMode(data, offset);\n        if (mode == null)\n            throw new InvalidDataException(\"Expected non-null addressing mode\");\n            \n        var format = GetInstructionFormatString(data, offset);\n        var mnemonic = GetMnemonic(data, offset);\n            \n        int numDigitsForOperand1 = 0, numDigitsForOperand2 = 0;\n        int? operandValue1 = null, operandValue2 = null;\n        var identified = false;\n        var overridesAllowed = false;\n            \n        switch (mode)\n        {\n            case Cpu65C816Constants.AddressMode.BlockMove:\n                identified = true;\n                numDigitsForOperand1 = numDigitsForOperand2 = 2;\n                operandValue1 = data.GetRomByte(offset + 1);\n                operandValue2 = data.GetRomByte(offset + 2);\n                break;\n            case Cpu65C816Constants.AddressMode.Constant8:\n            case Cpu65C816Constants.AddressMode.Immediate8:\n            case Cpu65C816Constants.AddressMode.Immediate16:\n                identified = true;\n                overridesAllowed = true;\n                numDigitsForOperand1 = mode == Cpu65C816Constants.AddressMode.Immediate16 \n                    ? 4 \n                    : 2;\n                operandValue1 = mode == Cpu65C816Constants.AddressMode.Immediate16 \n                    ? data.GetRomWord(offset + 1) \n                    : data.GetRomByte(offset + 1);\n                break;\n        }\n        \n        var operandOriginalStr1 = \"\";\n        var operandOriginalStr2 = \"\";\n        \n        if (!identified)\n        {\n            // note: lots of complexity with labels, mirroring, overrides, etc inside here:\n            operandOriginalStr1 = FormatOperandAddress(data, offset);\n            operandOriginalStr2 = \"\";\n        }\n        else\n        {\n            operandOriginalStr1 = operandValue1!=null ? CreateHexStr(operandValue1, numDigitsForOperand1) : \"\";\n            operandOriginalStr2 = operandValue2!=null ? CreateHexStr(operandValue2, numDigitsForOperand2) : \"\";\n        }\n\n        var operandFinalStr1 = operandOriginalStr1;\n        var operandFinalStr2 = operandOriginalStr2;\n        \n        // try a substitution, if any exist. only for opcodes with ONE operand (not going to handle the ones with two)\n        if (overridesAllowed)\n        {\n            var specialDirective = GetSpecialDirectiveOverrideFromComments(data, offset);\n            if (specialDirective != null)\n            {\n                if (!string.IsNullOrEmpty(specialDirective.TextToOverride))\n                {\n                    operandFinalStr1 = specialDirective.TextToOverride; // allow overriding here\n                }\n                else if (specialDirective.ConstantFormatOverride == CpuUtils.OperandOverride.FormatOverride.AsDecimal && operandValue1!=null)\n                {\n                    operandFinalStr1 = operandValue1.ToString();\n                }\n            }\n        }\n        \n        var finalStr = string.Format(format, mnemonic, operandFinalStr1, operandFinalStr2);\n        \n        var pointerStr = GetPointerStr(data, offset);\n        if (pointerStr != null)\n            finalStr = pointerStr;\n        \n        var outputInstructionData = new CpuInstructionDataFormatted  {\n            // generate a string like: \"LDA.W $01,X\" or \"JSR.W fn_do_stuff\"\n            FullGeneratedText = finalStr,\n            \n            // save these in case useful later\n            OriginalNonOverridenOperand1 = operandOriginalStr1,\n            OriginalNonOverridenOperand2 = operandOriginalStr2,\n            OverriddenOperand1 = operandFinalStr1,\n            OverriddenOperand2 = operandFinalStr2,\n            \n            // save other stuff if you want \n        };\n        \n        return outputInstructionData;\n    }\n\n    private static int SearchForRomOffsetBoundsOfPointerTableFrom(TByteSource data, int offset, bool searchBackwards = true)\n    {\n        // what type of pointer table are we in the middle of?\n        var pointerTableType = data.GetFlag(offset);\n        if (pointerTableType is not (FlagType.Pointer16Bit or FlagType.Pointer24Bit or FlagType.Pointer32Bit))\n            return -1;  // not a pointer table\n\n        var currentBound = offset;\n        while (true)\n        {\n            var candidateOffset = searchBackwards ? currentBound - 1 : currentBound + 1;\n            if (candidateOffset > data.GetRomSize() || candidateOffset < 0)\n                break;\n            \n            // must be marked as a pointer OF THE SAME TYPE\n            if (data.GetFlag(candidateOffset) !=  pointerTableType)\n                break;\n\n            currentBound = candidateOffset;\n        }\n        \n        return currentBound;\n    }\n\n    private static string? GetPointerStr(TByteSource data, int offset)\n    {\n        var pointerType = data.GetFlag(offset);\n        if (pointerType is not (FlagType.Pointer16Bit or FlagType.Pointer24Bit or FlagType.Pointer32Bit))\n            return null;\n\n        var pointerTableStartOffset = SearchForRomOffsetBoundsOfPointerTableFrom(data, offset, searchBackwards: true);\n        if (pointerTableStartOffset == -1)\n            return null;\n        \n        // ok, we're inside a pointer table.\n        // we don't want to display a string for every entry, but only the first of the N bytes of the pointer table\n        var stride = pointerType switch\n        {\n            FlagType.Pointer16Bit => 2,\n            FlagType.Pointer24Bit => 3,\n            FlagType.Pointer32Bit => 4,\n            // ReSharper disable once UnreachableSwitchArmDueToIntegerAnalysis\n            _ => -1,\n        };\n        // ReSharper disable once ConditionIsAlwaysTrueOrFalse\n        if (stride == -1)\n            return null; // shouldn't happen but\n        \n        // special: return empty string (NOT null) since we're in the upper bytes of a pointer table. we don't\n        // want to show any text here.\n        var distanceToStartOfTable = offset - pointerTableStartOffset;\n        if (distanceToStartOfTable % stride != 0)\n            return \"\"; \n        \n        var ia = data.GetIntermediateAddressOrPointer(offset);\n        if (ia == -1)\n            return null;\n\n        // we're in the middle of a pointer table AND in the right position.\n        // show some useful text, if available.\n        var labelAtIa = data.Labels.GetLabel(ia);\n        if (labelAtIa != null)\n            return labelAtIa.Name;\n        \n        var iaClipped = stride switch\n        {\n            2 => ia & 0xFFFF,\n            3 => ia & 0xFFFFFF,\n            _ => ia // same as 4\n        };\n\n        return RomUtil.ConvertNumToHexStr(iaClipped, stride);\n    }\n\n    public override int AutoStepSafe(TByteSource byteSource, int offset)\n    {\n        var cmd = new AutoStepper65816<TByteSource>(byteSource);\n        cmd.Run(offset);\n        return cmd.Offset;\n    }\n\n    private static string CreateHexStr(int? v, int numDigits)\n    {\n        if (numDigits == 0)\n            return \"\";\n\n        if (v == null)\n            throw new InvalidDataException(\"Expected non-null input value, got null\");\n            \n        return Util.NumberToBaseString((int) v, Util.NumberBase.Hexadecimal, numDigits, true);\n    }\n\n    public override int GetInstructionLength(TByteSource data, int offset)\n    {\n        var mode = GetAddressMode(data, offset);\n            \n        // not sure if this is the right thing. probably fine. if we hit this, we're in a weird mess anyway.\n        return mode == null ? 1 : GetInstructionLength(mode.Value);\n    }\n\n    // Find, and append, in/out points to any that current exist at this offset and its IA address\n    public override void MarkInOutPoints(TByteSource data, int offset)\n    {\n        var iaOffsetPc = CalculateInOutPointsFromOffset(data, offset, out var newIaInOutPoint, out var newOffsetInOutPoint);\n\n        // these will append the in/out points to existing data that's already there\n        data.SetInOutPoint(offset, newOffsetInOutPoint);\n        if (iaOffsetPc >= 0)\n            data.SetInOutPoint(iaOffsetPc, newIaInOutPoint);\n    }\n\n    private static int GetInstructionLength(Cpu65C816Constants.AddressMode mode)\n    {\n        switch (mode)\n        {\n            case Cpu65C816Constants.AddressMode.Implied:\n            case Cpu65C816Constants.AddressMode.Accumulator:\n                return 1;\n            case Cpu65C816Constants.AddressMode.Constant8:\n            case Cpu65C816Constants.AddressMode.Immediate8:\n            case Cpu65C816Constants.AddressMode.DirectPage:\n            case Cpu65C816Constants.AddressMode.DirectPageXIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageSIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageXIndexIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageSIndexIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.Relative8:\n                return 2;\n            case Cpu65C816Constants.AddressMode.Immediate16:\n            case Cpu65C816Constants.AddressMode.Address:\n            case Cpu65C816Constants.AddressMode.AddressXIndex:\n            case Cpu65C816Constants.AddressMode.AddressYIndex:\n            case Cpu65C816Constants.AddressMode.AddressIndirect:\n            case Cpu65C816Constants.AddressMode.AddressXIndexIndirect:\n            case Cpu65C816Constants.AddressMode.AddressLongIndirect:\n            case Cpu65C816Constants.AddressMode.BlockMove:\n            case Cpu65C816Constants.AddressMode.Relative16:\n                return 3;\n            case Cpu65C816Constants.AddressMode.Long:\n            case Cpu65C816Constants.AddressMode.LongXIndex:\n                return 4;\n            default:\n                return 1;\n        }\n    }\n\n    // this can print bytes OR labels. it can also deal with SOME mirroring and Direct Page addressing etc/etc\n    private string FormatOperandAddress(TByteSource data, int offset)\n    {\n        var mode = GetAddressMode(data, offset);\n        if (mode == null)\n            throw new InvalidDataException(\"Expected non-null addressing mode\");\n        \n        var allowLabelUsageHere = true;\n        \n        // -------------------------------------------------------------------\n        // OPTION 0: SPECIAL DIRECTIVES\n        // we'll let the user do weird special-case things with \"!!\" directives\n        // -------------------------------------------------------------------\n        var specialDirective = GetSpecialDirectiveOverrideFromComments(data, offset);\n        if (specialDirective != null)\n        {\n            // ZERO VALIDATION OF THIS TEXT. it's up to the user to get it right. have fun\n            if (!string.IsNullOrEmpty(specialDirective.TextToOverride))\n                return specialDirective.TextToOverride;\n\n            if (specialDirective.ForceOnlyShowRawHex)\n                allowLabelUsageHere = false;\n        }\n\n        // ---------------------------------------------------------------\n        // OPTION 1: Trying to find a label that is appropriate for\n        //           this location, and if so, use it\n        // ---------------------------------------------------------------\n        \n        if (allowLabelUsageHere)\n        {\n            var finalLabelExpressionToUse = GetFinalLabelExpressionToUse(data, offset);\n            if (!string.IsNullOrEmpty(finalLabelExpressionToUse))\n                return finalLabelExpressionToUse;\n        }\n\n        // ---------------------------------------------------------------\n        // OPTION 2: Couldn't find a decent label to use\n        //           We'll just print the raw hex number as a constant instead\n        // ---------------------------------------------------------------\n        return GetFormattedRawHexIa(data, offset);\n    }\n\n    private static string GetFormattedRawHexIa(TByteSource data, int offset)\n    {\n        // don't bake the directpage offset into this\n        var intermediateAddress = data.GetIntermediateAddress(offset);\n        \n        var mode = GetAddressMode(data, offset);\n        if (mode == null)\n            throw new InvalidDataException(\"Expected non-null addressing mode\");\n        \n        var numByteDigitsToDisplay = GetNumBytesToShow(mode.Value);\n        if (mode is Cpu65C816Constants.AddressMode.Relative8 or Cpu65C816Constants.AddressMode.Relative16)\n        {\n            var romWord = data.GetRomWord(offset + 1);\n            if (!romWord.HasValue)\n                return \"\";\n\n            intermediateAddress = (int)romWord;\n        }\n\n        return RomUtil.ConvertNumToHexStr(intermediateAddress, numByteDigitsToDisplay);\n    }\n\n    private string GetFinalLabelExpressionToUse(TByteSource data, int offset)\n    {\n        // important: setting \"resolve: true\" here bakes the DP offset into the IA.\n        // this is usually what we want for labels BUT we have to build an expression that bakes this back out if so. \n        var intermediateAddress = data.GetIntermediateAddress(offset, resolve: true);\n        if (intermediateAddress < 0)\n            return \"\";\n        \n        var mode = GetAddressMode(data, offset);\n        if (mode == null)\n            throw new InvalidDataException(\"Expected non-null addressing mode\");\n        \n        // first and easiest: is there a label for this absolute address AND are we allowed to use it? if so, we'll use that.\n        // this label will include the DirectPage offset built into the IA.\n        var candidateLabel = GetValidatedLabelNameForOffset(data, offset);\n\n        // secondly: if, we didn't find a label that matches our IA 1:1. BUT, is there a mirrored label that works here?\n        // example: you have a label defined for 7E0004, and our IA is 000004. those are a mirror of the same data so,\n        // it's OK to use the 7E label.\n        var unmirrorCorrectedDisplacement = 0;\n        if (AttemptToUnmirrorLabels && candidateLabel == null) \n            (unmirrorCorrectedDisplacement, candidateLabel) = GetUnmirroredLabelNameAndDisplacement(data, mode.Value, intermediateAddress);\n        \n        // got a good label entry for this SNES address.\n        // now, does this label have multiple entries and we have to pick one based on the surrounding context?\n        // if so, do pick one now.\n        var labelName = ResolveLabelNameWithContext(data, candidateLabel, offset);\n        if (labelName == \"\")\n            return \"\";\n\n        // this is to try and get more labels in the output by creating a mathematical expression\n        // for ASAR to use. only works if you have accurate 'D' register (direct page) set.\n        // usually only useful after you've done a lot of tracelog capture.\n        //\n        // if your Directpage register is set wrong, you'll get wrong/weird label names, or miss a label name.\n        // it will still COMPILE byte-identical, it'll just look weird to humans.\n        var directPageDisplacement = 0;\n        if (AttemptTouseDirectPageArithmeticInFinalOutput &&\n            mode is Cpu65C816Constants.AddressMode.DirectPage\n                 or Cpu65C816Constants.AddressMode.DirectPageXIndex\n                 or Cpu65C816Constants.AddressMode.DirectPageYIndex\n                 or Cpu65C816Constants.AddressMode.DirectPageIndirect\n                 or Cpu65C816Constants.AddressMode.DirectPageXIndexIndirect\n                 or Cpu65C816Constants.AddressMode.DirectPageIndirectYIndex\n                 or Cpu65C816Constants.AddressMode.DirectPageLongIndirect\n                 or Cpu65C816Constants.AddressMode.DirectPageLongIndirectYIndex)\n        {\n            // intermediateAddress already has the dp offset baked in at this point.\n            // HOWEVER, we need to build an expression that backs it out now.\n\n            // example:\n            // say we have a line where D = $0100\n            // the instruction says LDA.B $20\n            // the true address the SNES is going to load is at $120 ($100 + $20 = $120)\n            //\n            // if we add a label for Diz like \"player_health\" for $120, then we want the output line to have the word \"player_health\" in it.\n            // so what we'll do here is build an expression for asar that will look like:\n            // LDA.B player_health-$100 ; since D=100, player_health-$100 = $20, and $20 is what actually gets assembled here, which is what we want.\n\n            directPageDisplacement = data.GetDirectPage(offset);\n        }\n        \n        // everything is figured out, now actually render the final label text.\n        \n        // sometimes we need to add expressions to subtract out extra numbers to make the right\n        // final bytes so asar will generate byte-identical output. do that here.\n        // these will return \"\" if no need for extra math (which is the typical case)\n        var dpOffsetExprStr = GenerateDisplacementString(directPageDisplacement);\n        var unMirrorCorrectedOffsetStr = GenerateDisplacementString(unmirrorCorrectedDisplacement);\n        \n        return $\"{labelName}{unMirrorCorrectedOffsetStr}{dpOffsetExprStr}\";\n    }\n\n    private static string ResolveLabelNameWithContext(TByteSource data, IAnnotationLabel? candidateLabel, int offset)\n    {\n        if (candidateLabel == null)\n            return \"\";\n        \n        var snesAddress = data.ConvertPCtoSnes(offset);\n        if (candidateLabel.ContextMappings.Count <= 0 || snesAddress == -1) \n            return candidateLabel.Name;\n        \n        // find any applicable regions in the surrounding context of where we are in the ROM offset:\n        var applicableOrderedRegions = data.Regions\n            .Where(x => snesAddress >= x.StartSnesAddress && snesAddress <= x.EndSnesAddress)\n            .OrderBy(x => x.Priority)\n            .ToList();\n\n        foreach (var region in applicableOrderedRegions)\n        {\n            var matchingLabelContext = candidateLabel.ContextMappings\n                .FirstOrDefault(labelContext => labelContext.Context == region.ContextToApply);\n\n            if (matchingLabelContext == null) \n                continue;\n            \n            return matchingLabelContext.NameOverride;\n        }\n\n        // didn't find a valid override at this context. use the default name\n        return candidateLabel.Name;\n    }\n\n    private (int unmirrorCorrectedDisplacement, IAnnotationLabel? unmirroredLabel) GetUnmirroredLabelNameAndDisplacement(\n        TByteSource data, Cpu65C816Constants.AddressMode mode, int snesAddress)\n    {\n        // NOTE: BE REALLY CAREFUL: SearchForMirroredLabel() IS A PERFORMANCE-INTENSE and HEAVILY OPTIMIZED FUNCTION\n        var (mirroredLabelSnesAddress, mirrorLabelEntry) = SearchForMirroredLabel(data, snesAddress);\n\n        if (mirrorLabelEntry == null || mirroredLabelSnesAddress == -1)\n            return (0, null);\n\n        // we're good, use this label\n        // BUT: in some situations, the output instructions needs to generate the un-mirrored address in order to match\n        // the exact bytes in the original ROM.  so, we may need to offset back out the difference between the two mirrors.\n        // see https://github.com/IsoFrieze/DiztinGUIsh/issues/117 for an example\n        var numBytesToShow = GetNumBytesToShow(mode);\n        var mask = numBytesToShow switch\n        {\n            1 => 0xFF,\n            2 => 0xFFFF,\n            3 => 0xFFFFFF,\n            _ => -1\n        };\n        \n        // valid for this to be positive or negative\n        var unmirrorCorrectedDisplacement = 0;\n        if (mask > 0 && (mirroredLabelSnesAddress & mask) != (snesAddress & mask)) \n            unmirrorCorrectedDisplacement = mirroredLabelSnesAddress - snesAddress;\n\n        return (unmirrorCorrectedDisplacement, mirrorLabelEntry);\n    }\n\n    // generate a string suitable for use with Asar expression math.\n    // i.e. \"-$FF\" or \"+$0100\", etc\n    private static string GenerateDisplacementString(int amountToDisplace)\n    {\n        if (amountToDisplace == 0) \n            return \"\";\n        \n        // IMPORTANT: Asar doesn't allow any whitespace between any math expression terms. don't output spaces here,\n        // or anywhere that we use this expression.  i.e. \"A+B\" is valid, but \"A + B\" will throw an error.\n        var direction = amountToDisplace > 0 ? '-' : '+';\n        \n        // since we're handling the sign ourselves, we only want to print positive numbers\n        var absAmountToDisplace = Math.Abs(amountToDisplace);\n        \n        return $\"{direction}${absAmountToDisplace:X}\";\n    }\n\n    private static CpuUtils.OperandOverride? GetSpecialDirectiveOverrideFromComments(TByteSource data, int offset)\n    {\n        // here be dragons.  we'll let the user override anything they ever wanted to in the comments.\n        // there will be, for now, very little validation/etc.\n        var snesAddress = data.ConvertPCtoSnes(offset);\n        if (snesAddress == -1)\n            return null;\n        \n        // searches both ROM comments and comments from the label list\n        var comment = data.GetCommentText(snesAddress);\n        return CpuUtils.ParseCommentSpecialDirective(comment);\n    }\n\n    private static IAnnotationLabel? GetValidatedLabelNameForOffset(TByteSource data, int srcOffset)\n    {\n        var destinationIa = data.GetIntermediateAddress(srcOffset, true);\n        if (destinationIa < 0)\n            return null;\n        \n        var candidateLabel = data.Labels.GetLabel(destinationIa);\n        if (string.IsNullOrEmpty(candidateLabel?.Name))\n            return null;\n        \n        // some special cases related to +/- local labels:\n        \n        // is this a local label?  like \"+\". \"-\", \"++\", \"--\", etc?\n        if (!RomUtil.IsValidPlusMinusLabel(candidateLabel.Name)) \n            return candidateLabel;      // not local label, so we're good\n        \n        // this IS a local +/- label, so let's do some additional validation..\n        \n        var opcode = data.GetRomByte(srcOffset);\n        var opcodeIsBranch = \n            opcode == 0x80 || // BRA\n            opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 || // BPL BMI BVC BVS\n            opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0;   // BCC BCS BNE BEQ\n        // NOT going to do this for any JUMPs like JMP, JML, and also not BRL\n        \n        // don't allow local +/- labels unless the opcode is a branch\n        if (!opcodeIsBranch)\n            return null;\n    \n        // finally, if this IS a branch AND a +/- label,\n        // make sure the branch is in the correct direction\n        // (no other checks prevent this except right here).\n        // DIZ doesn't treat local labels special, so it's up\n        // to us to enforce this here:\n        var srcSnesAddress = data.ConvertPCtoSnes(srcOffset);\n        var branchDirectionIsForward = candidateLabel.Name[0] == '+';\n        \n        var validBranchDirection =\n            srcSnesAddress != destinationIa &&                            // infinite loop (branch to self) \n            branchDirectionIsForward == (srcSnesAddress < destinationIa); // trying to branch the wrong way\n\n        return validBranchDirection ? candidateLabel : null;\n    }\n\n    private (int labelAddress, IAnnotationLabel? labelEntry) SearchForMirroredLabel(TByteSource data, int snesAddress)\n    {\n        // WARNING: during assembly text export, this function is EXTREMELY performance intensive.\n        // PLEASE PROFILE before making any serious changes \n\n        // optimization: during exporting, this function is EXTREMELY performance intensive.\n        // let's try and use a smaller subset of labels to search.\n        // this will only be available in certain contexts, like when exporting assembly text.\n        var exporterCache = data.Labels.MirroredLabelCacheSearch;\n        if (exporterCache != null)\n            return exporterCache.SearchOptimizedForMirroredLabel(snesAddress);\n\n        // less optimized fallback version (does same thing as above, but uses all labels)\n        // this is used during normal operation (like scrolling around the grid)\n        foreach (var (labelAddress, labelEntry) in data.Labels.Labels)\n        {\n            if (!RomUtil.AreLabelsSameMirror(snesAddress, labelAddress)) \n                continue;\n\n            // we found a label that's in WRAM and matches the same WRAM address as our IA.\n            // that means, we found a mirrored address we could match here. let's do so now.\n            //\n            // NOTE: this may not cover every case correctly. Whatever we put here needs to assemble\n            // down to the original bytes in the ROM.\n            // we CAN use expressions/etc to make this work if we want.\n            // the point is so humans can see the labels, but the output assembly can be the original bytes\n            // TODO: might need to limit this to cases where there are 2 bytes in the IA only.\n            return (labelAddress, labelEntry);\n        }\n        \n        return (-1, null);\n    }\n\n    private string GetMnemonic(TByteSource data, int offset, bool showHint = true)\n    {\n        var mn = Cpu65C816Constants.Mnemonics[data.GetRomByteUnsafe(offset)];\n        if (!showHint) \n            return mn;\n\n        var mode = GetAddressMode(data, offset);\n        if (mode == null)\n            return mn;\n                \n        var count = GetNumBytesToShow(mode.Value);\n\n        if (mode is Cpu65C816Constants.AddressMode.Constant8 or Cpu65C816Constants.AddressMode.Relative16 or Cpu65C816Constants.AddressMode.Relative8) \n            return mn;\n\n        return count switch\n        {\n            1 => mn + \".B\",\n            2 => mn + \".W\",\n            3 => mn + \".L\",\n            _ => mn\n        };\n    }\n\n    private static int GetNumBytesToShow(Cpu65C816Constants.AddressMode mode)\n    {\n        switch (mode)\n        {\n            case Cpu65C816Constants.AddressMode.Constant8:\n            case Cpu65C816Constants.AddressMode.Immediate8:\n            case Cpu65C816Constants.AddressMode.DirectPage:\n            case Cpu65C816Constants.AddressMode.DirectPageXIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageSIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageXIndexIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageSIndexIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirect:\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirectYIndex:\n            case Cpu65C816Constants.AddressMode.Relative8:\n                return 1;\n            case Cpu65C816Constants.AddressMode.Immediate16:\n            case Cpu65C816Constants.AddressMode.Address:\n            case Cpu65C816Constants.AddressMode.AddressXIndex:\n            case Cpu65C816Constants.AddressMode.AddressYIndex:\n            case Cpu65C816Constants.AddressMode.AddressIndirect:\n            case Cpu65C816Constants.AddressMode.AddressXIndexIndirect:\n            case Cpu65C816Constants.AddressMode.AddressLongIndirect:\n            case Cpu65C816Constants.AddressMode.Relative16:\n                return 2;\n            case Cpu65C816Constants.AddressMode.Long:\n            case Cpu65C816Constants.AddressMode.LongXIndex:\n                return 3;\n        }\n        return 0;\n    }\n\n    // {0} = mnemonic\n    // {1} = intermediate address / label OR operand 1 for block move\n    // {2} = operand 2 for block move\n    private string GetInstructionFormatString(TByteSource data, int offset)\n    {\n        var mode = GetAddressMode(data, offset);\n        switch (mode)\n        {\n            case Cpu65C816Constants.AddressMode.Implied:\n                return \"{0}\";\n            case Cpu65C816Constants.AddressMode.Accumulator:\n                return \"{0} A\";\n            case Cpu65C816Constants.AddressMode.Constant8:\n            case Cpu65C816Constants.AddressMode.Immediate8:\n            case Cpu65C816Constants.AddressMode.Immediate16:\n                return \"{0} #{1}\";\n            case Cpu65C816Constants.AddressMode.DirectPage:\n            case Cpu65C816Constants.AddressMode.Address:\n            case Cpu65C816Constants.AddressMode.Long:\n            case Cpu65C816Constants.AddressMode.Relative8:\n            case Cpu65C816Constants.AddressMode.Relative16:\n                return \"{0} {1}\";\n            case Cpu65C816Constants.AddressMode.DirectPageXIndex:\n            case Cpu65C816Constants.AddressMode.AddressXIndex:\n            case Cpu65C816Constants.AddressMode.LongXIndex:\n                return \"{0} {1},X\";\n            case Cpu65C816Constants.AddressMode.DirectPageYIndex:\n            case Cpu65C816Constants.AddressMode.AddressYIndex:\n                return \"{0} {1},Y\";\n            case Cpu65C816Constants.AddressMode.DirectPageSIndex:\n                return \"{0} {1},S\";\n            case Cpu65C816Constants.AddressMode.DirectPageIndirect:\n            case Cpu65C816Constants.AddressMode.AddressIndirect:\n                return \"{0} ({1})\";\n            case Cpu65C816Constants.AddressMode.DirectPageXIndexIndirect:\n            case Cpu65C816Constants.AddressMode.AddressXIndexIndirect:\n                return \"{0} ({1},X)\";\n            case Cpu65C816Constants.AddressMode.DirectPageIndirectYIndex:\n                return \"{0} ({1}),Y\";\n            case Cpu65C816Constants.AddressMode.DirectPageSIndexIndirectYIndex:\n                return \"{0} ({1},S),Y\";\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirect:\n            case Cpu65C816Constants.AddressMode.AddressLongIndirect:\n                return \"{0} [{1}]\";\n            case Cpu65C816Constants.AddressMode.DirectPageLongIndirectYIndex:\n                return \"{0} [{1}],Y\";\n            case Cpu65C816Constants.AddressMode.BlockMove:\n                return \"{0} {1},{2}\";\n        }\n        return \"\";\n    }\n        \n    public static Cpu65C816Constants.AddressMode? GetAddressMode(TByteSource data, int offset)\n    {\n        var opcode = data.GetRomByte(offset);\n        if (!opcode.HasValue)\n            return null;\n            \n        var mFlag = data.GetMFlag(offset);\n        var xFlag = data.GetXFlag(offset);\n            \n        return GetAddressMode(opcode.Value, mFlag, xFlag);\n    }\n\n    public static Cpu65C816Constants.AddressMode GetAddressMode(int opcode, bool mFlag, bool xFlag)\n    {\n        var mode = Cpu65C816Constants.AddressingModes[opcode];\n        return mode switch\n        {\n            Cpu65C816Constants.AddressMode.ImmediateMFlagDependent => mFlag\n                ? Cpu65C816Constants.AddressMode.Immediate8\n                : Cpu65C816Constants.AddressMode.Immediate16,\n            Cpu65C816Constants.AddressMode.ImmediateXFlagDependent => xFlag\n                ? Cpu65C816Constants.AddressMode.Immediate8\n                : Cpu65C816Constants.AddressMode.Immediate16,\n            _ => mode\n        };\n    }\n}\n\npublic static class Cpu65C816Constants\n{\n    public enum AddressMode : byte\n    {\n        Implied, Accumulator, Constant8, Immediate8, Immediate16,\n        ImmediateXFlagDependent, ImmediateMFlagDependent,\n        DirectPage, DirectPageXIndex, DirectPageYIndex,\n        DirectPageSIndex, DirectPageIndirect, DirectPageXIndexIndirect,\n        DirectPageIndirectYIndex, DirectPageSIndexIndirectYIndex,\n        DirectPageLongIndirect, DirectPageLongIndirectYIndex,\n        Address, AddressXIndex, AddressYIndex, AddressIndirect,\n        AddressXIndexIndirect, AddressLongIndirect,\n        Long, LongXIndex, BlockMove, Relative8, Relative16\n    }\n\n    public static readonly string[] Mnemonics =\n    {\n        \"BRK\", \"ORA\", \"COP\", \"ORA\", \"TSB\", \"ORA\", \"ASL\", \"ORA\", \"PHP\", \"ORA\", \"ASL\", \"PHD\", \"TSB\", \"ORA\", \"ASL\", \"ORA\",\n        \"BPL\", \"ORA\", \"ORA\", \"ORA\", \"TRB\", \"ORA\", \"ASL\", \"ORA\", \"CLC\", \"ORA\", \"INC\", \"TCS\", \"TRB\", \"ORA\", \"ASL\", \"ORA\",\n        \"JSR\", \"AND\", \"JSL\", \"AND\", \"BIT\", \"AND\", \"ROL\", \"AND\", \"PLP\", \"AND\", \"ROL\", \"PLD\", \"BIT\", \"AND\", \"ROL\", \"AND\",\n        \"BMI\", \"AND\", \"AND\", \"AND\", \"BIT\", \"AND\", \"ROL\", \"AND\", \"SEC\", \"AND\", \"DEC\", \"TSC\", \"BIT\", \"AND\", \"ROL\", \"AND\",\n        \"RTI\", \"EOR\", \"WDM\", \"EOR\", \"MVP\", \"EOR\", \"LSR\", \"EOR\", \"PHA\", \"EOR\", \"LSR\", \"PHK\", \"JMP\", \"EOR\", \"LSR\", \"EOR\",\n        \"BVC\", \"EOR\", \"EOR\", \"EOR\", \"MVN\", \"EOR\", \"LSR\", \"EOR\", \"CLI\", \"EOR\", \"PHY\", \"TCD\", \"JML\", \"EOR\", \"LSR\", \"EOR\",\n        \"RTS\", \"ADC\", \"PER\", \"ADC\", \"STZ\", \"ADC\", \"ROR\", \"ADC\", \"PLA\", \"ADC\", \"ROR\", \"RTL\", \"JMP\", \"ADC\", \"ROR\", \"ADC\",\n        \"BVS\", \"ADC\", \"ADC\", \"ADC\", \"STZ\", \"ADC\", \"ROR\", \"ADC\", \"SEI\", \"ADC\", \"PLY\", \"TDC\", \"JMP\", \"ADC\", \"ROR\", \"ADC\",\n        \"BRA\", \"STA\", \"BRL\", \"STA\", \"STY\", \"STA\", \"STX\", \"STA\", \"DEY\", \"BIT\", \"TXA\", \"PHB\", \"STY\", \"STA\", \"STX\", \"STA\",\n        \"BCC\", \"STA\", \"STA\", \"STA\", \"STY\", \"STA\", \"STX\", \"STA\", \"TYA\", \"STA\", \"TXS\", \"TXY\", \"STZ\", \"STA\", \"STZ\", \"STA\",\n        \"LDY\", \"LDA\", \"LDX\", \"LDA\", \"LDY\", \"LDA\", \"LDX\", \"LDA\", \"TAY\", \"LDA\", \"TAX\", \"PLB\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",\n        \"BCS\", \"LDA\", \"LDA\", \"LDA\", \"LDY\", \"LDA\", \"LDX\", \"LDA\", \"CLV\", \"LDA\", \"TSX\", \"TYX\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",\n        \"CPY\", \"CMP\", \"REP\", \"CMP\", \"CPY\", \"CMP\", \"DEC\", \"CMP\", \"INY\", \"CMP\", \"DEX\", \"WAI\", \"CPY\", \"CMP\", \"DEC\", \"CMP\",\n        \"BNE\", \"CMP\", \"CMP\", \"CMP\", \"PEI\", \"CMP\", \"DEC\", \"CMP\", \"CLD\", \"CMP\", \"PHX\", \"STP\", \"JML\", \"CMP\", \"DEC\", \"CMP\",\n        \"CPX\", \"SBC\", \"SEP\", \"SBC\", \"CPX\", \"SBC\", \"INC\", \"SBC\", \"INX\", \"SBC\", \"NOP\", \"XBA\", \"CPX\", \"SBC\", \"INC\", \"SBC\",\n        \"BEQ\", \"SBC\", \"SBC\", \"SBC\", \"PEA\", \"SBC\", \"INC\", \"SBC\", \"SED\", \"SBC\", \"PLX\", \"XCE\", \"JSR\", \"SBC\", \"INC\", \"SBC\"\n    };\n\n    public static readonly AddressMode[] AddressingModes =\n    {\n        AddressMode.Constant8, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.DirectPage, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Accumulator, AddressMode.Implied,\n        AddressMode.Address, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n\n        AddressMode.Address, AddressMode.DirectPageXIndexIndirect, AddressMode.Long, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Accumulator, AddressMode.Implied,\n        AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n\n        AddressMode.Implied, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex,\n        AddressMode.BlockMove, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.BlockMove, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.Long, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n\n        AddressMode.Implied, AddressMode.DirectPageXIndexIndirect, AddressMode.Relative16, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Accumulator, AddressMode.Implied,\n        AddressMode.AddressIndirect, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.AddressXIndexIndirect, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n\n        AddressMode.Relative8, AddressMode.DirectPageXIndexIndirect, AddressMode.Relative16, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageYIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.Address, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n\n        AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageXIndexIndirect, AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageYIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.AddressYIndex, AddressMode.LongXIndex,\n\n        AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.DirectPageIndirect, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.AddressLongIndirect, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n\n        AddressMode.ImmediateXFlagDependent, AddressMode.DirectPageXIndexIndirect, AddressMode.Constant8, AddressMode.DirectPageSIndex,\n        AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPage, AddressMode.DirectPageLongIndirect,\n        AddressMode.Implied, AddressMode.ImmediateMFlagDependent, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.Address, AddressMode.Address, AddressMode.Address, AddressMode.Long,\n        AddressMode.Relative8, AddressMode.DirectPageIndirectYIndex, AddressMode.DirectPageIndirect, AddressMode.DirectPageSIndexIndirectYIndex,\n        AddressMode.Address, AddressMode.DirectPageXIndex, AddressMode.DirectPageXIndex, AddressMode.DirectPageLongIndirectYIndex,\n        AddressMode.Implied, AddressMode.AddressYIndex, AddressMode.Implied, AddressMode.Implied,\n        AddressMode.AddressXIndexIndirect, AddressMode.AddressXIndex, AddressMode.AddressXIndex, AddressMode.LongXIndex,\n    };\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/Cpu.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model;\n\nnamespace Diz.Cpu._65816;\n\npublic class Cpu<TByteSource> where TByteSource : IRomByteFlagsGettable, IRomByteFlagsSettable, ISnesAddressConverter, ISteppable \n{\n    /// <summary>\n    /// Interpret the bytes at Offset as an opcode + optional operands. Mark them as such,\n    /// and return the next offset (which should, if everything went well, be another instruction)\n    /// \n    /// </summary>\n    /// <param name=\"data\">bytesource to operate on</param>\n    /// <param name=\"offset\">starting offset in bytesource to operate on</param>\n    /// <param name=\"branch\">if true, and the instruction at offset is a branch, take the branch</param>\n    /// <param name=\"force\">\n    /// if true, ignore control flow statements (branches, returns, etc), and set next offset right after.\n    /// DANGEROUS and can cause you to run off the edge of a block of code and into data. usually avoid, unless you know what you're asking for</param>\n    /// <param name=\"prevOffset\">\n    /// If available, set this to the offset of the instruction located before this one.\n    /// Cpu state data like M,X,DB, and DP flags will be copied from the previous instruction.\n    /// </param>\n    /// <returns>The offset of the instruction located after this one, or returns the same offset if the Step operation failed or is not supported.</returns>\n    public virtual int Step(TByteSource data, int offset, bool branch, bool force, int prevOffset = -1) => offset;\n    \n    \n    public virtual int GetInstructionLength(TByteSource data, int offset) => 1;\n    public virtual int GetIntermediateAddress(TByteSource data, int offset, bool resolve) => -1;\n    public virtual void MarkInOutPoints(TByteSource data, int offset) {} // nop\n    public virtual int CalculateInOutPointsFromOffset(\n        TByteSource data,\n        int offset,\n        out InOutPoint newIaInOutPoint,\n        out InOutPoint newOffsetInOutPoint\n    )\n    {\n        newIaInOutPoint = InOutPoint.None;\n        newOffsetInOutPoint = InOutPoint.None;\n        return -1;\n    }\n\n    public virtual string GetInstructionStr(TByteSource data, int offset) => \"\";\n    \n    public virtual CpuInstructionDataFormatted GetInstructionData(TByteSource data, int offset) => new();\n    \n    public virtual int AutoStepSafe(TByteSource byteSource, int offset) => offset;\n\n    public int AutoStepHarsh(TByteSource byteSource, int offset, int amount)\n    {\n        var newOffset = offset;\n        var prevOffset = offset - 1;\n\n        while (newOffset < offset + amount)\n        {\n            var nextOffset = byteSource.Step(newOffset, false, true, prevOffset);\n            prevOffset = newOffset;\n            newOffset = nextOffset;\n        }\n\n        return newOffset;\n    }\n}\n\npublic class CpuSpc700<TByteSource> : Cpu<TByteSource> where TByteSource : IRomByteFlagsGettable, IRomByteFlagsSettable, ISnesAddressConverter, ISteppable\n{\n    // implement me       \n}\n    \npublic class CpuSuperFx<TByteSource> : Cpu<TByteSource> where TByteSource : IRomByteFlagsGettable, IRomByteFlagsSettable, ISnesAddressConverter, ISteppable\n{\n    // implement me\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/CpuDispatcher.cs",
    "content": "﻿using Diz.Core.Interfaces;\n\nnamespace Diz.Cpu._65816;\n\n// TODO: has a lot of memory allocation with this approach. if that becomes an issue, cache the creations or use lazy loading\n// TODO: this requires all data sources for any CPU to implement all interfaces for all CPUs, which is not going to\n//       work if we want to add more different CPU types later\n\npublic class CpuDispatcher\n{\n    public Cpu<SnesApi> Cpu(SnesApi data, int offset)\n    {\n        var arch = data.Data.GetArchitecture(offset);\n\n        return arch switch\n        {\n            Architecture.Cpu65C816 => new Cpu65C816<SnesApi>(),\n            Architecture.Apuspc700 => new CpuSpc700<SnesApi>(),\n            Architecture.GpuSuperFx => new CpuSuperFx<SnesApi>(),\n            _ => new Cpu<SnesApi>()\n        };\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/CpuUtils.cs",
    "content": "﻿namespace Diz.Cpu._65816;\n\npublic class CpuUtils\n{\n    public class OperandOverride\n    {\n        // completely override the operand text with this user-specified text\n        // this is the complete wild west: no checks will be done, etc.\n        public string TextToOverride { get; set; } = \"\";\n        \n        // if true, never print a label (always print the raw hex)\n        // useful for things like PEA or PER instructions which may falsely grab labels\n        public bool ForceOnlyShowRawHex { get; set; }\n        \n        // if true, then this particular label WONT create a temporary label\n        // at its original offset (useful for things like PTR_ or DATA_ destinations where\n        // the label value here is used for accessing memory that's really not related to it.\n        // for instance, if a game is doing \"LDA.L $C00000, X\", and accesisng lots of locations using \n        // different values in X, then, we might not want to stick a \"DATA_\" label at $C00000\n        public bool DontGenerateTemporaryLabelAtDestination { get; set; }\n\n        public enum FormatOverride\n        {\n            None,\n            AsDecimal,\n            // add more as desired\n        }\n\n        public enum IncSrcOverride\n        {\n            None,\n            IncSrcStart,\n            IncSrcEnd,\n        }\n\n        public FormatOverride ConstantFormatOverride { get; set; } = FormatOverride.None;\n        public IncSrcOverride IncludeSrc { get; set; } = IncSrcOverride.None;\n    }\n    \n    /// <summary>\n    /// Parse special override directives that may be present in a comment\n    /// These can override generated labels, or force no labels to be generated, among other things.\n    /// Typically, special directives start with \"!!\" and contain some commands \n    /// </summary>\n    /// <param name=\"inputText\"></param>\n    /// <returns></returns>\n    public static OperandOverride? ParseCommentSpecialDirective(string? inputText)\n    {\n        // TODO: allow multiple directives in a line separated by delimiter\n        \n        if (string.IsNullOrEmpty(inputText) || !inputText.StartsWith(\"!!\"))\n            return null;\n        \n        // filter anything after a semicolon, which we'll treat like a comment and ignore. trim leftover whitespace\n        var semicolonIndex = inputText.IndexOf(';');\n        if (semicolonIndex >= 0) {\n            inputText = inputText[..semicolonIndex].Trim();\n        }\n        if (string.IsNullOrEmpty(inputText))\n            return null;\n        \n        // Remove the \"!!\" prefix and continue processing\n        var cmd = inputText[2..];\n\n        // option 1: override real label or correct hex with whatever our text is\n        // (does ZERO checking to ensure it's valid, it's all up to the user now)\n        if (cmd.StartsWith(\"o \"))\n        {\n            var textToOverride = cmd[2..]; // skip \"o \"\n            var directive = new OperandOverride {\n                TextToOverride = textToOverride\n            };\n            \n            // normally, we'd be done right here, but, let's see if this seems to contain an expression. if so,\n            // we'll also disable this label from being able to create a temporary destination label (like DATA_xxx or PTR_xxx).\n            // (other uses of the same address might still create that destination temp label though)\n            // if this causes any issues, feel free to turn it off.\n            // feel free to add more expression checks here:\n            if (textToOverride.Contains('!') || textToOverride.Contains('+') || textToOverride.Contains('-'))\n                directive.DontGenerateTemporaryLabelAtDestination = true;\n            \n            return directive;\n        }\n\n        // option 2: force output to never allow a label on this line \n        if (cmd.StartsWith('n'))\n        {\n            return new OperandOverride {\n                ForceOnlyShowRawHex = true\n            };\n        }\n        \n        // option3: create an incsrc directive from this, as though it's a Region.\n        // must have a \"is\" and a \"ie\" tag (include start, include end) and a label on the start, for this to work\n        // this is just a lazier way to define incsrc directives (instead of having to create a Region and use the \"export as new file\" option)\n        if (cmd.StartsWith('i') && cmd.Length == 2)\n        {\n            return cmd[1] switch\n            {\n                's' => new OperandOverride { IncludeSrc = OperandOverride.IncSrcOverride.IncSrcStart },\n                'e' => new OperandOverride { IncludeSrc = OperandOverride.IncSrcOverride.IncSrcEnd },\n                _ => null\n            };\n        }\n        \n        // option 3: (if applicable) force this constant to show up as Decimal instead of Hex.\n        // like \"!!fd\" = format as decimal\n        if (cmd.StartsWith('f') && cmd.Length == 2)\n        {\n            if (cmd[1] == 'd')\n            {\n                return new OperandOverride {\n                    ConstantFormatOverride = OperandOverride.FormatOverride.AsDecimal\n                };   \n            }\n        }\n\n        return null;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/CpuVectorTable.cs",
    "content": "﻿using Diz.Cpu._65816.import;\n\nnamespace Diz.Cpu._65816;\n\npublic static class CpuVectorTable\n{\n    // = FFE0 (start of vector table) minus FFD5 (start of settings after the cart title, it's the offset of the ROM map mode in the header).\n    // vector table may be in different ROM locations for different rom map modes, but, it's always the same offset within the cart header.\n    // in this file we only care about relative offset to the start of the tables\n    public const int VectorTableSettingsOffset = 11;\n    public record VectorRomEntry(int AbsoluteRomOffset, VectorTableEntry VectorTableEntry);\n    public record VectorTableEntry(string Name, int VectorTableOffset);\n    \n    private static readonly List<VectorTableEntry> VectorTableEntries =\n            // these must be kept in order, with no gaps, starting from the start of the vector table.\n            // we're going to include all of the vector table entries, including the stuff that's not used by the 65816 CPU,\n            // and also the stuff not used by the SNES hardware\n            new List<string> {\n                // Native Mode Vectors (snes $FFE0-$FFEF)\n                SnesVectorNames.Native_Reserved1__ignored,\n                SnesVectorNames.Native_Reserved2__ignored,\n                SnesVectorNames.Native_COP,\n                SnesVectorNames.Native_BRK,\n                SnesVectorNames.Native_ABORT,\n                SnesVectorNames.Native_NMI,\n                SnesVectorNames.Native_RESET__ignored,\n                SnesVectorNames.Native_IRQ,\n            \n                // Emulation Mode Vectors (snes $FFF0-$FFFF)\n                SnesVectorNames.Emulation_Reserved1__ignored,\n                SnesVectorNames.Emulation_Reserved2__ignored,\n                SnesVectorNames.Emulation_COP,\n                SnesVectorNames.Emulation_Reserved3__ignored,\n                SnesVectorNames.Emulation_ABORT,\n                SnesVectorNames.Emulation_NMI,\n                SnesVectorNames.Emulation_RESET,\n                SnesVectorNames.Emulation_IRQBRK\n            }\n                .Select((name, index) => new VectorTableEntry(\n                    Name: name, \n                    // relative offset from the start of the vector table, starting from 00 and going up by 2 bytes\n                    VectorTableOffset: index*2\n                ))\n                .ToList();\n    \n    public static IEnumerable<VectorRomEntry> ComputeVectorTableNamesAndOffsets(int settingsAbsoluteRomOffset)\n    {\n        // compute the absolute ROM address of all vector table entries (including the invalid ones)\n        // settingsAbsoluteRomOffset is the ROM address of the start of the \"snes settings\" i.e. the value after the cart title.\n        // examples: ROM offset 0xFFD5 for hirom, 0x7FD5 for lorom \n        return VectorTableEntries.Select(entry => \n            new VectorRomEntry(\n                AbsoluteRomOffset: settingsAbsoluteRomOffset + VectorTableSettingsOffset + entry.VectorTableOffset, \n                VectorTableEntry: entry\n            ));\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/DataAddSnesApiDecorator.cs",
    "content": "﻿using Diz.Core;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model.snes;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816;\n\n[UsedImplicitly]\npublic class DataAddSnesApiDecorator : IDataFactory\n{\n    private readonly IDataFactory baseDataFactory;\n    private readonly Func<IData, ISnesData> createSnesApi;\n\n    public DataAddSnesApiDecorator(IDataFactory baseDataFactory, Func<IData, ISnesData> createSnesApi)\n    {\n        this.baseDataFactory = baseDataFactory;\n        this.createSnesApi = createSnesApi;\n    }\n\n    public Data Create()\n    {\n        var data = baseDataFactory.Create();\n        data.Apis.AddIfDoesntExist(createSnesApi(data));\n        return data;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/SampleRomData.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Core;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\nusing Diz.Core.util;\n\nnamespace Diz.Cpu._65816;\n\npublic class SnesSampleRomDataFactory : ISampleDataFactory\n{\n    private readonly IDataFactory dataFactory;\n    public SnesSampleRomDataFactory(IDataFactory dataFactory)\n    {\n        this.dataFactory = dataFactory;\n    }\n\n    public static string GetSampleUtf8CartridgeTitle() => \"｢ﾎ｣ abcｦｧｨ TEST123\"; // don't pad here\n    \n    public Data Create()\n    {\n        var data = dataFactory.Create();\n\n        data.RomMapMode = RomMapMode.LoRom;\n        data.RomSpeed = RomSpeed.FastRom;\n\n        // random sample code I made up; hopefully it shows a little bit of\n        // everything so you can see how the settings will effect the output\n        data.RomBytes = new RomBytes\n        {\n            new() { Rom = 0x78, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, Point = InOutPoint.InPoint },\n            new() { Rom = 0xA9, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true },\n            new() { Rom = 0x01, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x8D, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true },\n            new() { Rom = 0x0D, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x42, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x5C, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, Point = InOutPoint.EndPoint },\n            new() { Rom = 0x0A, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand },\n            new() { Rom = 0xC2, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, Point = InOutPoint.InPoint },\n            new() { Rom = 0x30, TypeFlag = FlagType.Operand },\n            new() { Rom = 0xA9, TypeFlag = FlagType.Opcode },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x21, TypeFlag = FlagType.Operand },\n            new() { Rom = 0x5B, TypeFlag = FlagType.Opcode },\n            new() { Rom = 0x4B, TypeFlag = FlagType.Opcode, DirectPage = 0x2100 },\n            new() { Rom = 0xAB, TypeFlag = FlagType.Opcode, DirectPage = 0x2100 },\n            new() { Rom = 0xA2, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x07, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0xBF, TypeFlag = FlagType.Opcode, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100\n            },\n            new() { Rom = 0x32, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x9F, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x7E, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xCA, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xCA, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0x10, TypeFlag = FlagType.Opcode, Point = InOutPoint.OutPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0xF4, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x40, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x41, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x42, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x64, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x43, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0xAE, TypeFlag = FlagType.Opcode, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100\n            },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0xFC, TypeFlag = FlagType.Opcode, Point = InOutPoint.OutPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x3A, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0x4C, TypeFlag = FlagType.Opcode, Point = InOutPoint.EndPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xC0, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0x00, TypeFlag = FlagType.Data16Bit, Point = InOutPoint.ReadPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x08, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x10, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x20, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Data16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0x44, TypeFlag = FlagType.Pointer16Bit, Point = InOutPoint.ReadPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x80, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x7B, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x80, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x44, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x81, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xC4, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x81, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x0A, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x82, TypeFlag = FlagType.Pointer16Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0x08, TypeFlag = FlagType.Opcode, Point = InOutPoint.InPoint, DataBank = 0x80, DirectPage = 0x2100\n            },\n            new() { Rom = 0x8B, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x4B, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xAB, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xE2, TypeFlag = FlagType.Opcode, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x20, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xC2, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x10, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xA2, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x1F, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n\n            // --------------------------\n            // highlighting a particular section here\n            // we will use this for unit tests as well.\n\n            // LDA.W Test_Data,X\n            new()\n            {\n                Rom = 0xBD, TypeFlag = FlagType.Opcode, MFlag = true, Point = InOutPoint.InPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x5B, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 }, // Test_Data\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 }, // Test_Data\n\n            // STA.W $0100,X\n            new() { Rom = 0x9D, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x01, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n\n            // DEX\n            new() { Rom = 0xCA, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n\n            // BPL CODE_80804F\n            new()\n            {\n                Rom = 0x10, TypeFlag = FlagType.Opcode, MFlag = true, Point = InOutPoint.OutPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0xF7, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n\n            // ------------------------------------\n\n            new() { Rom = 0xAB, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x28, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n            new()\n            {\n                Rom = 0x60, TypeFlag = FlagType.Opcode, Point = InOutPoint.EndPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n\n            // --------------------------\n\n            new()\n            {\n                Rom = 0x45, TypeFlag = FlagType.Data8Bit, Point = InOutPoint.ReadPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x8D, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x69, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x83, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xB2, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x99, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x23, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x01, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xA3, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xF8, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x52, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x08, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xBB, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x29, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x5C, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x32, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xE7, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x88, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x3C, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x30, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x18, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x9A, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xB0, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x34, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x8C, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xDD, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x05, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0xB7, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x83, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x34, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x6D, TypeFlag = FlagType.Data8Bit, DataBank = 0x80, DirectPage = 0x2100 },\n        };\n        data.Comments = new SortedDictionary<int, string>\n        {\n            { 0x808000 + 0x03, \"this sets FastROM\" },\n            { 0x808000 + 0x0F, \"direct page = $2100\" },\n            { 0x808000 + 0x21, \"clear APU regs\" },\n            { 0x808000 + 0x44, \"this routine copies Test_Data to $7E0100\" }\n        };\n\n        foreach (var (k, v) in new Dictionary<int, Label>\n            {\n                {\n                    0x808000 + 0x00, new Label { Name = \"Emulation_RESET\", Comment = \"Sample emulation reset location\" }\n                },\n                { 0x808000 + 0x0A, new Label { Name = \"FastRESET\", Comment = \"Sample label\" } },\n                { 0x808000 + 0x32, new Label { Name = \"Test_Indices\" } },\n                { 0x808000 + 0x3A, new Label { Name = \"Pointer_Table\" } },\n                { 0x808000 + 0x44, new Label { Name = \"First_Routine\" } },\n                { 0x808000 + 0x5B, new Label { Name = \"Test_Data\", Comment = \"Pretty cool huh?\" } }\n            })\n        {\n            data.Labels.AddLabel(k, v);\n        }\n\n        PostProcess(data);\n        return data;\n    }\n\n    private static int PadRomBytesUpTo(Data data, int numBytesToPadUpTo)\n    {\n        var originalRomSizeBeforePadding = data.RomBytes.Count;\n        while (data.RomBytes.Count < numBytesToPadUpTo)\n            data.RomBytes.Add(new RomByte());\n\n        return originalRomSizeBeforePadding;\n    }\n\n    private static void Pad(Data data)\n    {\n        // tricky: this sample data can be used to populate the \"sample assembly output\"\n        // window to demo some features. One thing we'd like to demo is showing generated\n        // labels that reach into \"unreached\" code (i.e. labels like \"UNREACH_XXXXX\")\n        //\n        // To accomplish this, we'll pad the size of the sample ROM data to 32k, but,\n        // we'll tell the assembly exporter to limit to the first couple hundred bytes by\n        // only assembling bytes up to the original amount (not the padded amount)\n        const int numBytesToPadUpTo = 0x8000;\n        var originalRomSizeBeforePadding = PadRomBytesUpTo(data, numBytesToPadUpTo);\n        \n        data.Tags.AddIfDoesntExist(new SampleDataGenerationTag\n        {\n            OriginalRomSizeBeforePadding = originalRomSizeBeforePadding\n        });\n    }\n\n    private static void PostProcess(Data data)\n    {\n        Pad(data);\n        \n        var snesApi = data.GetSnesApi();\n        Debug.Assert(snesApi != null);\n\n        // inject the game name into the bytes\n        // This is a UTF8 string that needs to be converted to ShiftJIS (Ascii w/some japanese chars) encoding.\n        snesApi.SetCartridgeTitle(GetSampleUtf8CartridgeTitle());\n\n        // initialize some SNES header stuff (this is not complete, feel free to add things that are useful)\n        Debug.Assert(snesApi.RomMapMode == RomMapMode.LoRom);\n        Debug.Assert(snesApi.RomSpeed == RomSpeed.FastRom);\n        var romSettingsOffset = RomUtil.GetRomSettingOffset(RomMapMode.LoRom);\n        const int loromAndFastRom = 0x30;\n        data.RomBytes[romSettingsOffset].Rom = loromAndFastRom;\n\n        // do this LAST after all modifications to the ROM bytes have been completed \n        snesApi.FixChecksum();\n\n        // NORMALLY when a project is loaded, we have to open the ROM file on disk and read the bytes on disk into RomBytes\n        // for this sample data, there is no ROM on disk, so we tell Diz we already took care of it\n        data.RomBytesLoaded = true;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/ServiceRegistration.cs",
    "content": "﻿using Diz.Core;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Cpu._65816.import;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.Cpu._65816;\n\n[UsedImplicitly]\npublic class DizCpu65816ServiceRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.Register<IAddRomDataCommand, AddRomDataCommand>();\n\n        // when we create a IData registration, add a SNES API component to it\n        serviceRegistry.Decorate<IDataFactory, DataAddSnesApiDecorator>();\n\n        serviceRegistry.Register<ImportRomSettings, IProjectFactoryFromRomImportSettings>((factory, settings) =>\n            new SnesProjectFactoryFromRomImportSettings(\n                factory.GetInstance<IProjectFactory>(),\n                settings));\n\n        serviceRegistry.Register<IData, ISnesData>(CreateSnesApiWithData);\n        \n        serviceRegistry.Register<IProjectImportDefaultSettingsFactory, SnesDefaultSettingsFactory>();\n        serviceRegistry.Register<ISnesRomImportSettingsBuilder, SnesRomImportSettingsBuilder>();\n\n        serviceRegistry.Register<ISnesRomAnalyzer, SnesRomAnalyzer>();\n        serviceRegistry.Register<IVectorTableCache, CachedVectorTableEntries>();\n        \n        RegisterMigrations(serviceRegistry);\n\n        RegisterSampleDataServices(serviceRegistry);\n    }\n\n    private static ISnesData CreateSnesApiWithData(IServiceFactory serviceFactory, IData data) =>\n        new SnesApi(data);\n\n    private static void RegisterMigrations(IServiceRegistry serviceRegistry)\n    {\n        // list all SNES-specific migrations here (there can be multiple migration classes here,\n        // they'll be applied by their internal ordering)\n        //\n        // note: for registration it's important to give each of these a unique NAME so we can get all of them and apply in order.\n        serviceRegistry.Register<IMigration, MigrationBugfix050JapaneseText>(\"migrate_100_to_101\");\n        serviceRegistry.Register<IMigration>(_ => new MigrationNoOp { AppliesToSaveVersion = 101 }, \"migrate_101_to_102\");\n        serviceRegistry.Register<IMigration>(_ => new MigrationNoOp { AppliesToSaveVersion = 102 }, \"migrate_102_to_103\");\n        serviceRegistry.Register<IMigration>(_ => new MigrationNoOp { AppliesToSaveVersion = 103 }, \"migrate_103_to_104\");\n    }\n\n    private static void RegisterSampleDataServices(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.Register<ISampleDataFactory, SnesSampleRomDataFactory>();\n        serviceRegistry.Register<IDataFactory, SnesSampleRomDataFactory>(\"SampleData\");\n        \n        // TODO: does the below line work instead of the two above?\n        // serviceRegistry.Register<ISampleDataFactory, SnesSampleRomDataFactory>(\"SampleData\");\n\n        serviceRegistry.Register(CreateSampleProject, \"SampleProject\");\n\n        serviceRegistry.Register<ISnesSampleProjectFactory>(factory =>\n            new SnesSampleProjectFactory(\n                factory.GetInstance<IProjectFactory>(\"SampleProject\")\n            ));\n    }\n\n    private static IProjectFactory CreateSampleProject(IServiceFactory factory) => \n        new ProjectFactory(factory.GetInstance<IDataFactory>(\"SampleData\"));\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/SnesData.cs",
    "content": "﻿using System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing Diz.Core;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\n\nnamespace Diz.Cpu._65816;\n\npublic interface ISnesChecksum\n{\n    public int RomSettingsOffset { get; }\n\n    public uint RomComplement { get; }\n    public uint RomChecksum { get; }\n    public uint RomCheckSumsFromRomBytes { get; }\n\n    public int RomComplementOffset { get; }\n    public int RomChecksumOffset { get; }\n    \n    public ushort ComputeChecksum();\n    public bool ComputeIsChecksumValid();\n\n    public void FixChecksum();\n}\n\npublic interface ISnesCartName\n{\n    public int CartridgeTitleStartingOffset { get; }\n    public string CartridgeTitleName { get; }\n}\n\npublic interface IDataUtilities\n{\n    int FixMisalignedFlags();\n    void NormalizeWramLabels();\n}\n\npublic interface IMiscNavigable\n{\n    public (int unreachedOffsetFound, int iaSourceAddress) \n        FindNextUnreachedBranchPointAfter(\n            int startingOffset, \n            bool searchForward = true, \n            bool includeUntakenBranchPoints = true, \n            bool requireUnreached = true\n        );\n    \n    public int DetectNextPointerTableFromAddressingModeUsageAfter(\n        int startingOffset, \n        bool searchForward = true\n    );\n}\n\npublic interface ISnesApi<out TData> :\n    IRomByteFlagsGettable, \n    IRomByteFlagsSettable, \n    ISnesAddressConverter, \n    ISteppable,\n    IMiscNavigable,\n    IAutoSteppable,\n    ISnesIntermediateAddress, \n    IInOutPointSettable, \n    IInOutPointGettable,\n    IReadOnlyByteSource,\n    IReadOnlyLabels,\n    IRomMapProvider,\n    IRomSize,\n    ISnesBankInfo,\n    ISnesChecksum,\n    ISnesCartName,\n    IInstructionGettable,\n    IDataUtilities,\n    IArchitectureApi,\n    IMarkOperandAndOpcode,\n    ICommentTextProvider,\n    IRegionProvider\n\n    where TData : IData\n{\n    TData Data { get; }\n\n    public void CacheVerificationInfoFor(ISnesCachedVerificationInfo verificationCache);\n}\n\npublic interface ISnesData : ISnesApi<IData>\n{\n}\n\npublic class SnesApi : ISnesData\n{\n    public IData Data { get; }\n    \n    public SnesApi(IData data)\n    {\n        Data = data;\n    }\n\n    public int RomSettingsOffset => RomUtil.GetRomSettingOffset(Data.RomMapMode);\n    public int RomComplementOffset => RomSettingsOffset + 0x07; // 2 bytes - complement\n    public int RomChecksumOffset => RomComplementOffset + 2; // 2 bytes - checksum\n        \n    public int CartridgeTitleStartingOffset => \n        RomUtil.GetCartridgeTitleStartingRomOffset(RomSettingsOffset);\n\n    public string CartridgeTitleName =>\n        RomUtil.GetCartridgeTitleFromBuffer(\n            Data.GetRomBytes(CartridgeTitleStartingOffset, RomUtil.LengthOfTitleName)\n        );\n    \n    // recalculates the checksum and then modifies the internal bytes in the ROM so it contains\n    // the valid checksum in the ROM header.\n    //\n    // NOTE: this new checksum is [currently] never saved with the project file / serialized (since we don't\n    // store the potentially copyrighted ROM bytes in the project file). it should just be used for\n    // testing/verification purposes. (that is why this is protected, it's not part of the normal API)\n    public void FixChecksum()\n    {\n        var rawRomBytesCopy = Data.RomBytes.CreateListRawRomBytes();\n        ChecksumUtil.UpdateRomChecksum(rawRomBytesCopy, Data.RomMapMode, GetRomSize());\n        Data.RomBytes.SetBytesFrom(rawRomBytesCopy, 0);\n    }\n\n    // looks at the actual bytes present in the ROM and calculates their checksum\n    // this is unrelated to any stored/cached checksums in the Project file. \n    public ushort ComputeChecksum() => \n        (ushort) ChecksumUtil.ComputeChecksumFromRom(Data.RomBytes.CreateListRawRomBytes());\n        \n    public bool ComputeIsChecksumValid() =>\n        ChecksumUtil.IsRomChecksumValid(Data.RomBytes.CreateListRawRomBytes(), Data.RomMapMode, GetRomSize());\n\n    public uint RomComplement => (uint) Data.GetRomWord(RomComplementOffset)!;\n    public uint RomChecksum => (uint) Data.GetRomWord(RomChecksumOffset)!;\n    public uint RomCheckSumsFromRomBytes => (RomChecksum << 16) | RomComplement;\n\n    public FlagType GetFlag(int i) => Data.RomBytes[i].TypeFlag;\n    public void SetFlag(int i, FlagType flag) => Data.RomBytes[i].TypeFlag = flag;\n\n    public InOutPoint GetInOutPoint(int i) => Data.RomBytes[i].Point;\n\n    public void SetInOutPoint(int i, InOutPoint point) => Data.RomBytes[i].Point |= point;\n    public void ClearInOutPoint(int i) => Data.RomBytes[i].Point = 0;\n    public int GetDataBank(int i) => Data.RomBytes[i].DataBank;\n    public void SetDataBank(int i, int dBank) => Data.RomBytes[i].DataBank = (byte)dBank;\n    public int GetDirectPage(int i) => Data.RomBytes[i].DirectPage;\n    public void SetDirectPage(int i, int dPage) => Data.RomBytes[i].DirectPage = 0xFFFF & dPage;\n    public bool GetXFlag(int i) => Data.RomBytes[i].XFlag;\n    public void SetXFlag(int i, bool x) => Data.RomBytes[i].XFlag = x;\n    public bool GetMFlag(int i) => Data.RomBytes[i].MFlag;\n    public void SetMFlag(int i, bool m) => Data.RomBytes[i].MFlag = m;\n    public int GetMxFlags(int i)\n    {\n        return (Data.RomBytes[i].MFlag ? 0x20 : 0) | (Data.RomBytes[i].XFlag ? 0x10 : 0);\n    }\n    public void SetMxFlags(int i, int mx)\n    {\n        Data.RomBytes[i].MFlag = ((mx & 0x20) != 0);\n        Data.RomBytes[i].XFlag = ((mx & 0x10) != 0);\n    }\n\n    public int ConvertPCtoSnes(int offset) => \n        RomUtil.ConvertPCtoSnes(offset, Data.RomMapMode, Data.RomSpeed);\n\n    public int ConvertSnesToPc(int address) => \n        RomUtil.ConvertSnesToPc(address, Data.RomMapMode, GetRomSize());\n\n    public int GetIntermediateAddressOrPointer(int offset)\n    {\n        switch (GetFlag(offset))\n        {\n            case FlagType.Unreached:\n            case FlagType.Opcode:\n                return GetIntermediateAddress(offset, true);\n            case FlagType.Pointer16Bit:\n                var romWord = Data.GetRomWord(offset);\n                if (!romWord.HasValue)\n                    return -1;\n                \n                // the original way: use what is written in the UI\n                var bank = GetDataBank(offset); // allows overriding from the UI, but, you HAVE to set this correctly\n                \n                // but if it's zero (which can be a valid bank but usually won't be\n                // then, autodetect the bank from the bank we're in.\n                if (bank == 0)\n                {\n                    // new way, assumes the bank is the same as the location of the pointer\n                    // more useful as long as it doesn't break anything\n                    var snesAddrAtOffset = ConvertPCtoSnes(offset);\n                    if (snesAddrAtOffset != -1)\n                        bank = RomUtil.GetBankFromSnesAddress(snesAddrAtOffset);\n                }\n\n                return (bank << 16) | (int)romWord;\n            case FlagType.Pointer24Bit:\n            case FlagType.Pointer32Bit:\n                var romLong = Data.GetRomLong(offset);\n                if (!romLong.HasValue)\n                    return -1;\n                    \n                return (int)romLong;\n        }\n        return -1;\n    }\n    \n    public bool IsMatchingIntermediateAddress(int intermediateAddress, int addressToMatch)\n    {\n        var intermediateAddressOrPointer = GetIntermediateAddressOrPointer(intermediateAddress);\n        var destinationOfIa = ConvertSnesToPc(intermediateAddressOrPointer);\n\n        return destinationOfIa == addressToMatch;\n    }\n\n    public int GetRomSize() => \n        Data.RomBytes?.Count ?? 0;\n\n    public int GetBankSize() => \n        RomUtil.GetBankSize(Data.RomMapMode);\n\n    public int GetNumberOfBanks()\n    {\n        var bankSize = GetBankSize();\n        return bankSize == 0 \n            ? 0 \n            : GetRomSize() / bankSize;\n    }\n\n    public string GetBankName(int bankIndex)\n    {\n        var bankSnesByte = GetSnesBankByte(bankIndex);\n        return Util.NumberToBaseString(bankSnesByte, Util.NumberBase.Hexadecimal, 2);\n    }\n\n    private int GetSnesBankByte(int bankIndex)\n    {\n        var bankStartingPcOffset = bankIndex << 16;\n        var bankSnesNumber = ConvertPCtoSnes(bankStartingPcOffset) >> 16;\n        return bankSnesNumber;\n    }\n    \n    private Cpu<SnesApi> GetCpu(int offset) => \n        new CpuDispatcher().Cpu(this, offset);\n\n    public int GetInstructionLength(int offset) => \n        GetCpu(offset).GetInstructionLength(this, offset);\n    \n    public int Step(int offset, bool branch, bool force=false, int prevOffset=-1) =>\n        GetCpu(offset).Step(this, offset, branch, force, prevOffset);\n\n    public int AutoStepSafe(int offset) =>\n        GetCpu(offset).AutoStepSafe(this, offset);\n\n    public int AutoStepHarsh(int offset, int count) =>\n        GetCpu(offset).AutoStepHarsh(this, offset, count);\n\n    public void MarkAsOpcodeAndOperandsStartingAt(int offset, int? dataBank = null, int? directPage = null, bool? xFlag = null, bool? mFlag = null)\n    {\n        if (GetCpu(offset) is not Cpu65C816<SnesApi> cpu65816)\n            return;\n\n        cpu65816.MarkAsOpcodeAndOperandsStartingAt(this, offset, dataBank, directPage, xFlag, mFlag);\n    }\n\n    // look for instructions using address modes that rely on pointer tables. identify any that may be incorrectly marked\n    // in order to assist the user in manually marking them correctly.\n    //\n    // this is intended for instructions like:\n    // JSR.W (#$81A224,X)       or JMP.W\n    // where we want to mark the IA address [81A224] as a 16-bit pointer.\n    // these are often really useful things to track down in disassembly, and otherwise a ton of manual work.\n    // this is a nice shortcut to uncovering and marking these quickly.\n    //\n    // pointer tables we find will look like this:\n    // PTR16_81A224:\n    //  dw some_function_ptrtable_00\n    //  dw some_function_ptrtable_02\n    //  dw some_function_ptrtable_04\n    //  ... etc ....\n    //\n    // return -1 if none found, or, ROM offset (PC) of possible pointer table located \n    public int DetectNextPointerTableFromAddressingModeUsageAfter(\n        // where to start searching from\n        int startingOffset, \n        \n        // direction to search\n        bool searchForward = true)\n    {\n        var snesData = Data.GetSnesApi();\n        if (snesData == null)\n            return -1;\n        \n        var direction = searchForward ? 1 : -1;\n        \n        for (\n            var offsetToTry = startingOffset + direction;\n            offsetToTry >= 0 && offsetToTry < Data.RomBytes.Count;\n            offsetToTry += direction\n        )\n        {\n            var opcodeRomByte = Data.RomBytes[offsetToTry];\n\n            // we want to find only reached opcodes using certain addressing modes. skip anything not matching the criteria \n            if (opcodeRomByte.TypeFlag != FlagType.Opcode)\n                continue;\n            \n            var addressMode = Cpu65C816<ISnesData>.GetAddressMode(snesData, offsetToTry);\n            \n            // note: feel free to add more address mode checks, if applicable.\n            // this is the main one most pointer table stuff seems to want to use\n            if (addressMode is not Cpu65C816Constants.AddressMode.AddressXIndexIndirect)\n                continue;\n            \n            // note: we could also filter to certain instructions, if we want.  JSR, JMP are going to be the main ones we care about\n            \n            // OK, we are on an instruction that MIGHT be one that uses a pointer table.\n            // let's keep narrowing it down:\n            \n            // does this instruction reference a valid IA? (Indirect address)\n            var iaSnesAddress = snesData.GetIntermediateAddress(offsetToTry);\n            if (iaSnesAddress == -1)\n                continue;\n            \n            // note: this will rule out pointer tables that are in RAM\n            // [which is OK because we're mostly marking ROM, but, could lead to false negatives if we're being thorough]\n            var iaOffsetPc = snesData.ConvertSnesToPc(iaSnesAddress);\n            if (iaOffsetPc == -1)\n                continue;\n            \n            // let's have a look at the destination the IA refers to.\n            // does the intermediate address match our criteria?\n            var iaRomByte = Data.RomBytes[iaOffsetPc];\n            \n            // so ACTUALLY, this destination is already marked correctly, so we can skip it from our search\n            // (remember: our goal is trying to locate INCORRECT or UNREACHED pointer tables)\n            if (iaRomByte.TypeFlag == FlagType.Pointer16Bit)\n                continue; // it's already good, SKIP IT\n            \n            // alright, we have a match! this is likely a pointer table but not marked as one.\n            return iaOffsetPc;\n        }\n\n        // got to the end of the ROM and didn't find anything\n        // this DOESN'T MEAN they don't exist, just, we couldn't detect anything\n        return -1;\n    }\n    \n    public (int unreachedOffsetFound, int iaSourceAddress) FindNextUnreachedBranchPointAfter(\n        // where to start searching from\n        int startingOffset, \n        \n        // direction to search\n        bool searchForward = true,\n        \n        // if true, return opcodes directly following a branch statement\n        bool includeUntakenBranchPoints = true,\n        \n        // if true, require any result to be an unreached point. if false, it can be reached\n        bool requireUnreached = true\n    )\n    {\n        var direction = searchForward ? 1 : -1;\n        for (\n            var offsetToTry = startingOffset + direction; \n            offsetToTry >= 0 && offsetToTry < Data.RomBytes.Count; \n            offsetToTry += direction\n        ) {\n            var romByte = Data.RomBytes[offsetToTry];\n            \n            // usually: require it to be unreached, or most of the time we don't care about it.\n            // (we're usually trying to quickly uncover new parts of the code we can step through that were missed before) \n            if (requireUnreached && romByte.TypeFlag != FlagType.Unreached)\n                continue;\n            \n            // operation #1: is this an uncovered opcode directly after a branch or jump that returns to this point?\n            if (includeUntakenBranchPoints)\n            {\n                // search backwards up to max of 4 bytes to find a previously marked opcode (if one exists and is already uncovered)\n                for (var i = 1; i <= 4; i++)\n                {\n                    var searchOffset = offsetToTry - i;\n                    \n                    if (searchOffset < 0 && searchOffset >= Data.RomBytes.Count)\n                        break; // out of bounds, bail\n                    \n                    var searchRomByte = Data.RomBytes[searchOffset];\n                    \n                    // if we fid something not fully uncovered yet, it's too risky, so bail\n                    if (searchRomByte.TypeFlag == FlagType.Unreached) \n                        break;\n                    \n                    // we're looking for something already marked as an opcode\n                    // if we hit operands, we want to keep searching backwards\n                    if (searchRomByte.TypeFlag != FlagType.Opcode)\n                        continue;\n                    \n                    // found our opcode. does it qualify as a conditional branch/subroutine call?\n                    // this isn't going to be foolproof but it should catch 95% of the stuff we most care about\n                    // we're ignoring: JMP JML BRA BRL (because they don't return to this point)\n                    var opcode = searchRomByte.Rom;\n                    var opcode_returns_after_jump = \n                        opcode == 0x10 || opcode == 0x30 || opcode == 0x50 || opcode == 0x70 ||     // BPL BMI BVC BVS\n                        opcode == 0x90 || opcode == 0xB0 || opcode == 0xD0 || opcode == 0xF0 ||     // BCC BCS BNE BEQ\n                        opcode == 0x20 || opcode == 0x22;   // JSR JSL\n\n                    if (opcode_returns_after_jump)\n                    {\n                        // GOT IT! this is our answer, we're done.\n                        return (offsetToTry, -1);\n                    }\n                    else\n                    {\n                        // we found an opcode searching backwards but, it's not a conditional branch / function call,\n                        // which means we should stop this search right here and move on.\n                        break;\n                    }\n                }\n            }\n            \n            // operation #2: does jump to US?\n            if ((romByte.Point & InOutPoint.InPoint) == 0)\n                continue;\n\n            // this is a legit in point, but, is the place it came FROM legit? return true if at least one legit\n            // opcode from the origin of this point.\n            // (warning: kinda expensive search, we have to scan EVERYTHING top to bottom)\n            // consider some caching of references when we create in/out points to save this.\n            \n            // search entire ROM for already marked opcodes whose IA jumps land on US.\n            // TODO: this search could later be extracted to form the basis of a \"find references to X address\" calculator\n            for (var i = 0; i < GetRomSize(); i++)\n            {\n                // we're looking for the OPCODE whose indirect address matches our candidate.\n                if (GetFlag(i) != FlagType.Opcode)\n                    continue;\n                \n                var cpu = GetCpu(i);\n                var iaOffsetPc = cpu.CalculateInOutPointsFromOffset(this, i, out var newIaInOutPoint, out var newOffsetInOutPoint);\n                \n                // does the intermediate address of this other location match the offset we're searching for\n                if (iaOffsetPc == -1 || iaOffsetPc != offsetToTry)\n                    continue;\n\n                var sourceIsOurInPoint = (newOffsetInOutPoint & InOutPoint.OutPoint) != 0 && (newIaInOutPoint & InOutPoint.InPoint) != 0;\n                if (!sourceIsOurInPoint)\n                    continue;\n                \n                // there may be other IAs that are ALSO this instructions inpoint, but, we can stop once we have found the first one.\n                // we could do some other stuff here like set M/X flags on our offset based on where it jumped/branched FROM.\n                // exercise for a later day.\n                // var flag = Data.RomBytes[i].MFlag   ...or...   .XFLag\n                // or could try:   Step(branch: true, offset: i);\n                // or could try:   GetCpuStateFor(offset, i, etc) <-- this probably is best to copy MX flags/etc to here.\n\n                return (offsetToTry, i);\n            }\n        }\n\n        // didn't find any\n        return (-1, -1);\n    }\n\n    // FIX ME: log and generation of dp opcodes. search references\n    public int GetIntermediateAddress(int offset, bool resolve = false) => \n        GetCpu(offset).GetIntermediateAddress(this, offset, resolve);\n\n    public string GetInstructionStr(int offset) => \n        GetCpu(offset).GetInstructionStr(this, offset);\n\n    public CpuInstructionDataFormatted GetInstructionData(int offset) =>\n        GetCpu(offset).GetInstructionData(this, offset);\n    \n    public void RescanInOutPoints()\n    {\n        for (var i = 0; i < GetRomSize(); i++)\n            ClearInOutPoint(i);\n\n        for (var i = 0; i < GetRomSize(); i++)\n        {\n            if (GetFlag(i) == FlagType.Opcode)\n            {\n                GetCpu(i).MarkInOutPoints(this, i);\n            }\n        }\n    }\n    \n    public int FixMisalignedFlags()\n    {\n        int numChanged = 0, romSize = GetRomSize();\n\n        for (var offset = 0; offset < romSize; offset++)\n        {\n            var flag = GetFlag(offset);\n\n            switch (flag)\n            {\n                case FlagType.Opcode:\n                {\n                    var bytesChanged = 0;\n                    var instructionLength = FixFlagsForOpcodeAndItsOperands(offset, romSize, ref bytesChanged);\n\n                    numChanged += bytesChanged;\n                    var newOffset = instructionLength - 1;\n                    \n                    offset += newOffset;\n                    break;\n                }\n                case FlagType.Operand:\n                    SetFlag(offset, FlagType.Opcode);\n                    numChanged++;\n                    offset--;\n                    break;\n                default:\n                {\n                    if (RomUtil.GetByteLengthForFlag(flag) > 1)\n                    {\n                        var step = RomUtil.GetByteLengthForFlag(flag);\n                        for (var j = 1; j < step; j++)\n                        {\n                            if (GetFlag(offset + j) == flag) \n                                continue;\n                            \n                            SetFlag(offset + j, flag);\n                            numChanged++;\n                        }\n                        offset += step - 1;\n                    }\n\n                    break;\n                }\n            }\n        }\n\n        return numChanged;\n    }\n\n    public void NormalizeWramLabels()\n    {\n        var wramLabels = Labels.Labels\n            .Where(x => RomUtil.GetWramAddressFromSnesAddress(x.Key) != -1)\n            .ToList();\n        \n        foreach (var label in wramLabels)\n        {\n            var normalizedSnesAddress = RomUtil.GetSnesAddressFromWramAddress(RomUtil.GetWramAddressFromSnesAddress(label.Key));\n\n            // already normalized? skip\n            if (normalizedSnesAddress == label.Key)\n                continue;\n\n            // if there are duplicates or overlaps, we can't proceed, they must be manually cleaned up\n            if (wramLabels.Any(x => x.Key == normalizedSnesAddress))\n                continue;\n\n            Data.Labels.RemoveLabel(label.Key);\n            Data.Labels.AddLabel(normalizedSnesAddress, label.Value, true);\n        }\n    }\n\n    private int FixFlagsForOpcodeAndItsOperands(int offset, int romSize, ref int bytesChanged)\n    {\n        var instructionLength = GetInstructionLength(offset);\n        for (var j = 1; j < instructionLength && offset + j < romSize; j++)\n        {\n            if (GetFlag(offset + j) == FlagType.Operand)\n                continue;\n                        \n            SetFlag(offset + j, FlagType.Operand);\n            bytesChanged++;\n        }\n\n        return instructionLength;\n    }\n\n    public byte? GetRomByte(int offset) => Data.GetRomByte(offset);\n    public int? GetRomWord(int offset) => Data.GetRomWord(offset);\n    public int? GetRomLong(int offset) => Data.GetRomLong(offset);\n    public int? GetRomDoubleWord(int offset) => Data.GetRomDoubleWord(offset);\n    public string GetCommentText(int snesAddress) => Data.GetCommentText(snesAddress);\n    public string? GetComment(int snesAddress) => Data.GetComment(snesAddress);\n    \n    public IReadOnlyLabelProvider Labels => Data.Labels;\n\n    public RomMapMode RomMapMode\n    {\n        get => Data.RomMapMode;\n        set => Data.RomMapMode = value;\n    }\n\n    public RomSpeed RomSpeed\n    {\n        get => Data.RomSpeed;\n        set => Data.RomSpeed = value;\n    }\n\n    public void CacheVerificationInfoFor(ISnesCachedVerificationInfo verificationCache)\n    {\n        // Save a copy of these identifying ROM bytes with the project file itself, so they'll\n        // be serialized to disk on project save. When we reload, we verify the recreated ROM data still matches both\n        // of these. If either are wrong, then the ROM on disk could be different from the one associated with the \n        // project.\n\n        // TODO: we need some way to abstract this SNES-specific stuff out of the project itself, while not breaking existing serialization\n        // TODO: consider replacing this with the new IDataTag interface, it's perfect for this.\n        verificationCache.InternalCheckSum = RomCheckSumsFromRomBytes;\n        verificationCache.InternalRomGameName = CartridgeTitleName;\n    }\n\n    public ObservableCollection<IRegion> Regions => Data.Regions;\n    public IRegion? GetRegion(int snesAddress) => Data.GetRegion(snesAddress);\n    public IRegion? CreateNewRegion() => Data.CreateNewRegion();\n}\n\npublic interface ISnesSampleProjectFactory : IProjectFactory\n{\n    \n}\n\npublic class SnesSampleProjectFactory : ISnesSampleProjectFactory\n{\n    private readonly IProjectFactory createNewProject;\n    public SnesSampleProjectFactory(IProjectFactory createNewProject)\n    {\n        this.createNewProject = createNewProject;\n    }\n\n    public IProject Create()\n    {\n        var project = createNewProject.Create() as Project; // TODO: don't cast, refactor to use IProject instead\n        Debug.Assert(project?.Data != null);\n        \n        var snesData = project.Data.GetSnesApi();\n        Debug.Assert(snesData != null);\n        \n        snesData.CacheVerificationInfoFor(project);\n        \n        return project;\n    }\n}\n\npublic static class SnesApiExtensions\n{\n    public static int MarkTypeFlag(this ISnesApi<IData> @this, int offset, FlagType type, int count)\n    {\n        return @this.Data.Mark(MarkAction, offset, count);\n\n        void MarkAction(int i)\n        {\n            // doing ONLY this is 100% fine, and is the original behavior\n            @this.SetFlag(i, type);\n            \n            // but also.... \n            // if we're marking pointers, it also helps give the LogWriter more hints if\n            // we reset the databank to the bank this byte is in.\n            // this matters most for 16-bit but doesn't hurt for other types.\n            // note: this is not always the best choice but, I think it's a good default.\n            // feel free to modify to suit your preferences\n            if (type is FlagType.Pointer16Bit or FlagType.Pointer24Bit or FlagType.Pointer32Bit)\n            {\n                var snesAddress = @this.ConvertPCtoSnes(i);\n                if (snesAddress != -1)\n                    @this.SetDataBank(i, RomUtil.GetBankFromSnesAddress(snesAddress));\n            }\n        }\n    }\n\n    public static int MarkDataBank(this ISnesApi<IData> @this, int offset, int db, int count) =>\n        @this.Data.Mark(i => @this.SetDataBank(i, db), offset, count);\n    \n    public static int MarkDirectPage(this ISnesApi<IData> @this, int offset, int dp, int count) => \n        @this.Data.Mark(i => @this.SetDirectPage(i, dp), offset, count);\n    \n    public static int MarkXFlag(this ISnesApi<IData> @this, int offset, bool x, int count) => \n        @this.Data.Mark(i => @this.SetXFlag(i, x), offset, count);\n    \n    public static int MarkMFlag(this ISnesApi<IData> @this, int offset, bool m, int count) => \n        @this.Data.Mark(i => @this.SetMFlag(i, m), offset, count);\n    \n    public static int MarkArchitecture(this ISnesApi<IData> @this, int offset, Architecture arch, int count) =>\n        @this.Data.Mark(i => @this.Data.SetArchitecture(i, arch), offset, count);\n    \n    // input can be any length, and will be padded, using spaces, to the right size for SNES header\n    public static void SetCartridgeTitle(this ISnesData @this, string utf8CartridgeTitle)\n    {\n        var rawShiftJisBytes = ByteUtil.GetRawShiftJisBytesFromStr(utf8CartridgeTitle);\n        var paddedShiftJisBytes = ByteUtil.PadCartridgeTitleBytes(rawShiftJisBytes);\n\n        // the BYTES need to be 21 in length. this is NOT the string length (which can be different because of multibyte chars)\n        Debug.Assert(paddedShiftJisBytes.Length == RomUtil.LengthOfTitleName);\n\n        @this.Data.RomBytes.SetBytesFrom(paddedShiftJisBytes, @this.CartridgeTitleStartingOffset);\n    }\n    \n    public static (int directPage, int dataBank, bool xFlag, bool mFlag) GetCpuStateAt(this IRomByteFlagsGettable @this, int offset) {\n        return (\n            @this.GetDirectPage(offset), \n            @this.GetDataBank(offset), \n            @this.GetXFlag(offset), \n            @this.GetMFlag(offset)\n        );\n    }\n    \n    public static \n        (byte? opcode, int directPage, int dataBank, bool xFlag, bool mFlag) \n        GetCpuStateFor<TByteSource>(this TByteSource @this, int offset, int prevOffset) \n        where TByteSource : \n        IReadOnlyByteSource,\n        IRomByteFlagsGettable\n    {\n        int directPage, dataBank;\n        bool xFlag, mFlag;\n        byte? opcode;\n\n        void SetCpuStateFromCurrentOffset()\n        {\n            opcode = @this.GetRomByte(offset);\n            (directPage, dataBank, xFlag, mFlag) = GetCpuStateAt(@this, offset);\n        }\n\n        void SetCpuStateFromPreviousOffset()\n        {\n            // go backwards from previous offset if it's valid but not an opcode\n            while (prevOffset >= 0 && @this.GetFlag(prevOffset) == FlagType.Operand)\n                prevOffset--;\n\n            // if we didn't land on an opcode, forget it\n            if (prevOffset < 0 || @this.GetFlag(prevOffset) != FlagType.Opcode) \n                return;\n            \n            // set these values to the PREVIOUS instruction\n            (directPage, dataBank, xFlag, mFlag) = GetCpuStateAt(@this, prevOffset);\n        }\n        \n        void SetMxFlagsFromRepSepAtOffset()\n        {\n            if (opcode != 0xC2 && opcode != 0xE2) // REP SEP \n                return;\n\n            var operand = @this.GetRomByte(offset + 1);\n            \n            xFlag = (operand & 0x10) != 0 ? opcode == 0xE2 : xFlag;\n            mFlag = (operand & 0x20) != 0 ? opcode == 0xE2 : mFlag;\n        }\n        \n        SetCpuStateFromCurrentOffset();     // set from our current position first\n        SetCpuStateFromPreviousOffset();    // if available, set from the previous offset instead.\n        SetMxFlagsFromRepSepAtOffset();\n\n        return (opcode, directPage, dataBank, xFlag, mFlag);\n    }\n    \n    public static ISnesData? GetSnesApi(this IData @this) => \n        @this.GetApi<ISnesData>();\n\n    public static (int found, string outputTextLog) GenerateMisalignmentReport(this ISnesApi<IData> @this)\n    {\n        // note: maybe this can be combined with FixMisalignedFlags() ? \n        \n        var outputTextLog = \"\";\n        int numMisalignedFound = 0, offset = 0;\n\n        while (numMisalignedFound < 500 && offset < @this.GetRomSize())\n        {\n            FlagType flag = @this.GetFlag(offset), check = flag == FlagType.Opcode ? FlagType.Operand : flag;\n            var step = flag == FlagType.Opcode\n                ? @this.GetInstructionLength(offset)\n                : RomUtil.GetByteLengthForFlag(flag);\n\n            var snesAddress = @this.ConvertPCtoSnes(offset);\n            \n            if (flag == FlagType.Operand)\n            {\n                numMisalignedFound++;\n                outputTextLog +=\n                    $\"{Util.NumberToBaseString(snesAddress, Util.NumberBase.Hexadecimal, 6, true)} \" +\n                    $\"(0x{Util.NumberToBaseString(offset, Util.NumberBase.Hexadecimal, 0)}): Operand without Opcode\\r\\n\";\n            }\n            else if (step > 1)\n            {\n                for (var i = 1; i < step; i++)\n                {\n                    if (@this.GetFlag(offset + i) == check) \n                        continue;\n                    \n                    numMisalignedFound++;\n                    var expected = Util.GetEnumDescription(check);\n                    var actual = Util.GetEnumDescription(@this.GetFlag(offset + i));\n                    \n                    outputTextLog += $\"{Util.NumberToBaseString(snesAddress, Util.NumberBase.Hexadecimal, 6, true)} \" +\n                            $\"(0x{Util.NumberToBaseString(offset + i, Util.NumberBase.Hexadecimal, 0)}): \" +\n                            $\"{actual} is not {expected}\\r\\n\";\n                }\n            }\n\n            offset += step;\n        }\n\n        if (numMisalignedFound == 0)\n            outputTextLog = \"No misaligned flags found!\";\n\n        return (numMisalignedFound, outputTextLog);\n    }\n}\n"
  },
  {
    "path": "Diz.Cpu.65816/src/import/CachedVectorTableEntries.cs",
    "content": "﻿namespace Diz.Cpu._65816.import;\n\n\npublic interface IVectorTableCacheData\n{\n    public List<CpuVectorTable.VectorRomEntry>? Entries { get; }\n    public int? RomSettingsOffsetUsed { get;  }   \n}\n\npublic interface IVectorTableCache : IVectorTableCacheData\n{\n    void RegenerateEntriesFor(int romSettingsOffset);\n    void Clear();\n}\n\n\npublic class CachedVectorTableEntries : IVectorTableCache\n{\n    public List<CpuVectorTable.VectorRomEntry>? Entries { get; private set; }\n    public int? RomSettingsOffsetUsed { get; private set; }\n\n    public void RegenerateEntriesFor(int romSettingsOffset)\n    {\n        if (RomSettingsOffsetUsed == romSettingsOffset)\n            return;\n        \n        Entries = CpuVectorTable.ComputeVectorTableNamesAndOffsets(romSettingsOffset).ToList();\n        RomSettingsOffsetUsed = romSettingsOffset;\n    }\n\n    public void Clear()\n    {\n        Entries = null;\n        RomSettingsOffsetUsed = null;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/ISnesRomImportSettingsBuilder.cs",
    "content": "﻿using System.ComponentModel;\nusing Diz.Core.Interfaces;\nusing Diz.Core.serialization;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816.import;\n\n/// <summary>\n/// Analyze a ROM, set some import options.\n/// Then generate settings that allow the rom to be imported (i.e. create a new Project from this ROM)\n/// </summary>\npublic interface ISnesRomImportSettingsBuilder : INotifyPropertyChanged\n{\n    public ISnesRomAnalyzerData Input { get; }\n    void Analyze(string romFilename);\n    void Analyze(byte[] rawRomBytes);\n    \n    bool OptionGenerateHeaderFlags { get; set; }\n    \n    // overrides the detection if necessary, may provide incorrect results\n    [UsedImplicitly] RomMapMode OptionSelectedRomMapMode { get; set; }\n    \n    bool OptionGenerateSelectedVectorTableLabels { get; set; }\n    public void OptionClearGenerateVectorTableLabels();\n    public void OptionSetGenerateVectorTableLabelFor(string vectorName, bool shouldGenerateLabel);\n\n    public ImportRomSettings GenerateSettings();\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/ImportRomSettingsBuilder.cs",
    "content": "﻿#nullable enable\n\nusing System.ComponentModel;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.project;\nusing Diz.Core.serialization;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816.import;\n\n[UsedImplicitly]\npublic class SnesRomImportSettingsBuilder : ISnesRomImportSettingsBuilder\n{\n    private bool optionGenerateHeaderFlags = true;\n    private RomMapMode optionSelectedRomMapMode;\n    private bool optionGenerateSelectedVectorTableLabels = true;\n    private readonly IReadFromFileBytes fileReader; \n\n    public ISnesRomAnalyzer Input { get; }\n\n    ISnesRomAnalyzerData ISnesRomImportSettingsBuilder.Input => Input;\n\n    public bool OptionGenerateHeaderFlags\n    {\n        get => optionGenerateHeaderFlags;\n        set => this.SetField(PropertyChanged, ref optionGenerateHeaderFlags, value);\n    }\n\n    public RomMapMode OptionSelectedRomMapMode\n    {\n        get => optionSelectedRomMapMode;\n        set => this.SetField(PropertyChanged, ref optionSelectedRomMapMode, value);\n    }\n\n    public bool OptionGenerateSelectedVectorTableLabels\n    {\n        get => optionGenerateSelectedVectorTableLabels;\n        set => this.SetField(PropertyChanged, ref optionGenerateSelectedVectorTableLabels, value);\n    }\n\n    private int? RomSettingOffset => \n        Input.AnalysisResults == null ? null : RomUtil.GetRomSettingOffset(Input.AnalysisResults.RomMapMode);\n\n    // ALL vector table entries (native and emulation) for the currently selected Rom Map Mode\n    // (including unused/deselected/etc)\n    private IVectorTableCache VectorTableForCurrentMapMode { get; }\n    \n    // a list of enabled vector table entries, varies with the UI.\n    private List<string> EnabledVectorEntries { get; } = [];\n\n    public SnesRomImportSettingsBuilder(ISnesRomAnalyzer snesRomAnalyzer, IVectorTableCache vectorTableCache, IReadFromFileBytes fileReader)\n    {\n        Input = snesRomAnalyzer;\n        VectorTableForCurrentMapMode = vectorTableCache;\n        this.fileReader = fileReader;\n\n        Input.PropertyChanged += InputOnPropertyChanged;\n        \n        Reset();\n    }\n\n    private void InputOnPropertyChanged(object? sender, PropertyChangedEventArgs e)\n    {\n        Reset();\n        RegenerateCachedVectorTableEntries();\n    }\n\n    private void RegenerateCachedVectorTableEntries()\n    {\n        var romSettingsOffset = RomSettingOffset;\n        if (!romSettingsOffset.HasValue)\n        {\n            VectorTableForCurrentMapMode.Clear();\n            return;\n        }\n        \n        VectorTableForCurrentMapMode.RegenerateEntriesFor(romSettingsOffset.Value);\n    }\n\n    public void Reset()\n    {\n        OptionGenerateHeaderFlags = true;\n    }\n\n    public void Analyze(string romFilename)\n    {\n        Reset();\n        var rawRomBytes = fileReader.ReadRomFileBytes(romFilename);\n        Input.Analyze(rawRomBytes, romFilename);\n        OnAnalyzed();\n    }\n\n    public void Analyze(byte[] rawRomBytes)\n    {\n        Reset();\n        Input.Analyze(rawRomBytes);\n        OnAnalyzed();\n    }\n\n    private void OnAnalyzed()\n    {\n        SetRomMapModeToAnalyzed();\n    }\n\n    private void SetRomMapModeToAnalyzed()\n    {\n        OptionSelectedRomMapMode = Input.AnalysisResults?.RomMapMode ?? RomMapMode.LoRom;\n    }\n\n    public void OptionClearGenerateVectorTableLabels()\n    {\n        EnabledVectorEntries.Clear();\n    }\n\n    public void OptionSetGenerateVectorTableLabelFor(string vectorName, bool shouldGenerateLabel)\n    {\n        var exists = EnabledVectorEntries.Contains(vectorName);\n\n        switch (shouldGenerateLabel)\n        {\n            case true when !exists:\n                EnabledVectorEntries.Add(vectorName);\n                break;\n            case false when exists:\n                EnabledVectorEntries.Remove(vectorName);\n                break;\n        }\n    }\n\n    public ImportRomSettings GenerateSettings()\n    {\n        if (Input.AnalysisResults == null || Input.RomBytes == null)\n            throw new InvalidOperationException(\"Can't create settings when analysis hasn't taken place yet\");\n\n        var settings = new ImportRomSettings\n        {\n            RomFilename = Input.Filename,\n            RomBytes = Input.RomBytes.ToList(),\n            RomMapMode = OptionSelectedRomMapMode,\n            RomSpeed = Input.AnalysisResults.RomSpeed,\n            InitialLabels = OptionGenerateSelectedVectorTableLabels ? GenerateVectorLabels() : new Dictionary<int, Label>()\n        };\n\n        if (OptionGenerateHeaderFlags)\n            settings.InitialHeaderFlags =\n                RomUtil.GenerateHeaderFlags(RomSettingOffset ?? -1, Input.RomBytes);\n\n        return settings;\n    }\n\n    private Dictionary<int, Label> GenerateVectorLabels()\n    {\n        var allEntries = VectorTableForCurrentMapMode.Entries ?? [];\n\n        return EnabledVectorEntries\n            .Select(x => allEntries.Single(entry => entry.VectorTableEntry.Name == x))\n            .Select(CreateLabelForVectorEntry)\n            .Where(x => x.HasValue)\n            .Select(x => x!.Value)\n            .ToDictionary(pair => pair.Key, pair => pair.Value);\n    }\n\n    private KeyValuePair<int, Label>? CreateLabelForVectorEntry(CpuVectorTable.VectorRomEntry entry)\n    {\n        if (!RomSettingOffset.HasValue)\n            return null;\n\n        // note: can also do a SNES address here if we wanted to. benefits to doing both.\n        // when mirroring works in labels, this will be useful to have both\n        var (romOffset, vectorTableEntry) = entry;\n\n        return new KeyValuePair<int, Label>(romOffset, new Label {\n            Name = vectorTableEntry.Name,\n        });\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/ImportUtils.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Core;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\n\nnamespace Diz.Cpu._65816.import;\n\npublic class SnesProjectFactoryFromRomImportSettings(\n    IProjectFactory baseProjectFactory,\n    IRomImportSettings importSettings)\n    : IProjectFactoryFromRomImportSettings\n{\n    public Project Read()\n    {\n        var project = baseProjectFactory.Create()\n            as Project; // TODO: refactor more, remove this cast, and have us return IProject directly \n        \n        Debug.Assert(project?.Data != null);\n\n        project.AttachedRomFilename = importSettings.RomFilename;\n        project.Session = new ProjectSession(project, \"\")\n        {\n            UnsavedChanges = true\n        };\n\n        var snesApi = project.Data.GetSnesApi();\n        Debug.Assert(snesApi != null);\n\n#if DIZ_3_BRANCH\n        // new way, though TODO we want to decouple the SNES stuff from here\n        project.Data.PopulateFrom(importSettings.RomBytes, importSettings.RomMapMode, importSettings.RomSpeed);\n#else\n        // old way\n        snesApi.RomMapMode = importSettings.RomMapMode;\n        snesApi.RomSpeed = importSettings.RomSpeed;\n        project.Data.RomBytes.CreateRomBytesFromRom(importSettings.RomBytes);\n#endif\n\n        foreach (var (romOffset, label) in importSettings.InitialLabels)\n        {\n            var snesAddress = snesApi.ConvertPCtoSnes(romOffset);\n            project.Data.Labels.AddLabel(snesAddress, label, true);\n        }\n\n        foreach (var (offset, flagType) in importSettings.InitialHeaderFlags)\n            snesApi.SetFlag(offset, flagType);\n\n        snesApi.CacheVerificationInfoFor(project);\n\n        return project;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/MigrationBugfix050JapaneseText.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816.import;\n\n// check for japanese character encoding bug in game title.\n// this is a result of us not storing XML correctly in earlier version of Diz, meaning we can't rely on\n// this bit of data in the project file to serve as an integrity check.  it's OK though because \n// checksums are unaffected, so as long as they match, we should be able to rely on that to complete our \n// validation checks.\n// https://github.com/Dotsarecool/DiztinGUIsh/issues/50\n[UsedImplicitly]\npublic sealed class MigrationBugfix050JapaneseText : IMigration\n{\n    public int AppliesToSaveVersion => 100;\n    private bool previousCartTitleMatchState;\n    private bool beforeAddRun;\n\n    public void OnLoadingBeforeAddLinkedRom(IAddRomDataCommand romAddCmd)\n    {\n        // this will have the loader skip checking the cart title name.\n        // we'll down our own check later.\n        previousCartTitleMatchState = romAddCmd.ShouldProjectCartTitleMatchRomBytes; \n        romAddCmd.ShouldProjectCartTitleMatchRomBytes = false;\n\n        beforeAddRun = true;\n    }\n\n    public void OnLoadingAfterAddLinkedRom(IAddRomDataCommand romAddCmd)\n    {\n        var project = Setup(romAddCmd);\n\n        if (!IsMitigationNeeded(project)) \n            return;\n\n        ApplyMitigation(project);\n    }\n\n    private Project Setup(IAddRomDataCommand romAddCmd)\n    {\n        Debug.Assert(beforeAddRun);\n\n        // we're called now after the romBytes have been loaded and all other checks are clear.\n        romAddCmd.ShouldProjectCartTitleMatchRomBytes = previousCartTitleMatchState;\n\n        return romAddCmd.Root?.Project ?? throw new InvalidOperationException();\n    }\n\n    private static void ApplyMitigation(IProject project)\n    {\n        // if the checksums match, but the internal ROM title doesn't, we'll assume we hit this bug and\n        // reset the project cartridge name from the actual bytes in the ROM.\n\n        // we're going a little overkill on checking here to make sure everything's good.\n        // NOTE: we're not checking for a VALID checksum, only that our various checksums match each other.\n        var snesData = project.Data.GetSnesApi() ?? throw new InvalidDataException(\"No SNES API for this data during Bugfix050Migration\");\n        \n        var checksumXmlVsBytesMatch = project.InternalCheckSum == snesData.RomCheckSumsFromRomBytes;\n        var checksumXmlVsCalculatedMatch = snesData.ComputeChecksum() == snesData.RomChecksum;\n\n        var allChecksumsGood = checksumXmlVsBytesMatch && checksumXmlVsCalculatedMatch;\n        if (!allChecksumsGood)\n            throw new InvalidDataException(\n                \"Migration to save file format version 101: Rom checksums from project and rom bytes don't match. Can't continue.\");\n\n        // ok, assume we hit the bug. to fix the broken save data, we will now\n        // re-cache the verification info. it will be stored correctly on the next serialize.\n        snesData.CacheVerificationInfoFor(project);\n    }\n\n    private static bool IsMitigationNeeded(IProject project)\n    {\n        var cartridgeTitleFromRom = project.InternalRomGameName; // this won't be correct if we hit the bug\n        var deserializedRomCartridgeTitle =\n            project.Data.GetSnesApi()?.CartridgeTitleName; // this will be correct, even if we hit the bug\n            \n        // we need to mitigate the bug if our titles don't agree with each other.\n        return deserializedRomCartridgeTitle != cartridgeTitleFromRom;\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/MigrationNoOp.cs",
    "content": "﻿using Diz.Core.serialization.xml_serializer;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816.import;\n\n// A migration for savedata format that \"does nothing\"\n// used when you don't actually need to change anything, but you do need to pull in a step that upgrades from a previous version\n// i.e. if v100 didn't save WidgetFooBars, and v101 optionally has WidgetFooBars in it,\n// then, we need something that bumps the version# from 100 to 101 (i.e. this class), but,\n// v100 will never have any WidgetFooBars, so we never need to look for them.  we simply just bump the ver#\n[UsedImplicitly]\npublic sealed class MigrationNoOp : IMigration\n{\n    // you MUST set this when instantiating though\n    public int AppliesToSaveVersion { get; init; } = -1;\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/SnesDefaultSettingsFactory.cs",
    "content": "﻿using Diz.Core.model;\nusing Diz.Core.serialization;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816.import;\n\n[UsedImplicitly]\npublic class SnesDefaultSettingsFactory : IProjectImportDefaultSettingsFactory\n{\n    private readonly ISnesRomImportSettingsBuilder snesRomImportSettingsBuilder;\n\n    public SnesDefaultSettingsFactory(ISnesRomImportSettingsBuilder snesRomImportSettingsBuilder)\n    {\n        this.snesRomImportSettingsBuilder = snesRomImportSettingsBuilder;\n    }\n\n    public IRomImportSettings Create(string romFilename)\n    {\n        // automated headless helper method to use all default settings and pray it works\n        // no GUI or anything. use with caution, only if you know what you're doing\n        \n        snesRomImportSettingsBuilder.Analyze(romFilename);\n        return snesRomImportSettingsBuilder.GenerateSettings(); // plug and pray....\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/SnesRomAnalyzer.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Runtime.CompilerServices;\nusing Diz.Core.Interfaces;\nusing Diz.Core.util;\nusing JetBrains.Annotations;\n\nnamespace Diz.Cpu._65816.import;\n\npublic interface ISnesRomAnalyzerData : INotifyPropertyChanged\n{\n    public record SnesRomAnalysisResults(\n        bool DetectedRomMapModeCorrectly, \n        RomMapMode RomMapMode,\n        RomSpeed RomSpeed\n    );\n    \n    public string? Filename { get; }\n    public IReadOnlyList<byte>? RomBytes { get; }\n    public SnesRomAnalysisResults? AnalysisResults { get; }\n    public int? RomSettingsOffset { get; }\n}\n\npublic interface ISnesRomAnalyzer : ISnesRomAnalyzerData\n{\n    public void Analyze(string romFilename);\n    public void Analyze(IReadOnlyList<byte> romBytes, string? romFilename = null);\n}\n\n\npublic class SnesRomAnalyzer : ISnesRomAnalyzer\n{\n    public string? Filename\n    {\n        get => filename;\n        private set => this.SetField(PropertyChanged, ref filename, value);\n    }\n    private string? filename;\n\n    public IReadOnlyList<byte>? RomBytes\n    {\n        get => romBytes;\n        private set => this.SetField(PropertyChanged, ref romBytes, value);\n    }\n    private IReadOnlyList<byte>? romBytes;\n\n    public ISnesRomAnalyzer.SnesRomAnalysisResults? AnalysisResults\n    {\n        get => analysisResults;\n        private set => this.SetField(PropertyChanged, ref analysisResults, value);\n    }\n    private ISnesRomAnalyzer.SnesRomAnalysisResults? analysisResults;\n\n    public int? RomSettingsOffset =>\n        AnalysisResults != null \n            ? RomUtil.GetRomSettingOffset(AnalysisResults.RomMapMode) \n            : null;\n\n    public void Reset()\n    {\n        Filename = null;\n        AnalysisResults = null;\n        RomBytes = null;\n    }\n\n    public void Analyze(string romFilename)\n    {\n        Reset();\n\n        var rawRomBytes = RomUtil.ReadRomFileBytes(romFilename);\n        Analyze(rawRomBytes);\n\n        Filename = romFilename;\n    }\n\n    public void Analyze(IReadOnlyList<byte> rawRomBytes, string? romFilename = null)\n    {\n        Reset();\n        AnalysisResults = DetectSettingsFor(rawRomBytes);\n        RomBytes = rawRomBytes;\n        Filename = romFilename;\n    }\n\n    private static ISnesRomAnalyzer.SnesRomAnalysisResults DetectSettingsFor(IReadOnlyList<byte> romBytes)\n    {\n        var romMapMode = RomUtil.DetectRomMapMode(romBytes, out var detectedRomMapModeCorrectly);\n\n        if (!detectedRomMapModeCorrectly)\n        {\n            // normal detection failed. any cart-specific exceptions?\n            if (RomUtil.DetectRomMapModeBustedGames(romBytes, out var detectedRomMapMode, out var detectedRomSpeed))\n            {\n                return new ISnesRomAnalyzer.SnesRomAnalysisResults(\n                    DetectedRomMapModeCorrectly: true, \n                    RomMapMode: detectedRomMapMode, \n                    RomSpeed: detectedRomSpeed\n                );\n            }\n        }\n        \n        return new ISnesRomAnalyzer.SnesRomAnalysisResults(\n            DetectedRomMapModeCorrectly: detectedRomMapModeCorrectly, \n            RomMapMode: romMapMode, \n            RomSpeed: RomUtil.GetRomSpeed(romMapMode, romBytes)\n        );\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    [NotifyPropertyChangedInvocator]\n    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)\n    {\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n    }\n}"
  },
  {
    "path": "Diz.Cpu.65816/src/import/SnesVectorNames.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\n\nnamespace Diz.Cpu._65816.import;\n\n[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\npublic static class SnesVectorNames\n{\n    // Note: All of these are valid 65816 vectors,\n    // but not all are actually used by the SNES hardware.\n\n    // Native Mode Vectors ($FFE0-$FFEF)\n    public const string Native_Reserved1__ignored = \"Native_Reserved1__ignored\";     // $FFE0 - reserved for future use, also unused by SNES\n    public const string Native_Reserved2__ignored = \"Native_Reserved2__ignored\";     // $FFE2 - reserved for future use, also unused by SNES\n    public const string Native_COP = \"Native_COP\";                                   // $FFE4 - ! USED by SNES !\n    public const string Native_BRK = \"Native_BRK\";                                   // $FFE6 - ! USED by SNES !\n    public const string Native_ABORT = \"Native_ABORT__ignored\";                      // $FFE8 - unused by SNES\n    public const string Native_NMI = \"Native_NMI\";                                   // $FFEA - ! USED by SNES ! - important\n    public const string Native_RESET__ignored = \"Native_RESET__ignored\";             // $FFEC - unused by SNES - native reset vector\n    public const string Native_IRQ = \"Native_IRQ\";                                   // $FFEE -  ! USED by SNES ! - important\n\n    // Emulation Mode Vectors ($FFF0-$FFFF)\n    public const string Emulation_Reserved1__ignored = \"Emulation_Reserved1__ignored\"; // $FFF0 - reserved for future use, also unused by SNES\n    public const string Emulation_Reserved2__ignored = \"Emulation_Reserved2__ignored\"; // $FFF2 - reserved for future use, also unused by SNES\n    public const string Emulation_COP = \"Emulation_COP\";                               // $FFF4 - unused by SNES\n    public const string Emulation_Reserved3__ignored = \"Emulation_Reserved3__ignored\"; // $FFF6 - reserved for future use, also unused by SNES\n    public const string Emulation_ABORT = \"Emulation_ABORT__ignored\";                  // $FFF8 - unused by SNES\n    public const string Emulation_NMI = \"Emulation_NMI\";                               // $FFFA - unused by SNES\n    public const string Emulation_RESET = \"Emulation_RESET\";                           // $FFFC - ! USED by SNES ! - main entry point. most ROMs only care about this one and ignore the rest of emulation vectors\n    public const string Emulation_IRQBRK = \"Emulation_IRQBRK\";                         // $FFFE - IRQ and BRK\n}"
  },
  {
    "path": "Diz.Import/Diz.Import.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n        <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\Diz.Core\\Diz.Core.csproj\" />\n      <ProjectReference Include=\"..\\Diz.Cpu.65816\\Diz.Cpu.65816.csproj\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Import/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"LightInject\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"FluentValidation\": \"[11.9.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"JetBrains.Profiler.SelfApi\": \"[2.5.0, )\",\n          \"LightInject\": \"[6.6.4, )\",\n          \"SharpZipLib\": \"[1.4.2, )\",\n          \"System.Diagnostics.Tracing\": \"[4.3.0, )\",\n          \"System.Resources.Extensions\": \"[8.0.0, )\",\n          \"System.Text.Encoding.CodePages\": \"[8.0.0, )\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      },\n      \"diz.cpu.65816\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Import/src/AssemblyInfo.cs",
    "content": "﻿using Diz.Import;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizImportServiceRegistration))]"
  },
  {
    "path": "Diz.Import/src/LabelImporter.cs",
    "content": "﻿using System.Globalization;\nusing System.Text.RegularExpressions;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\nusing Diz.Import.bsnes;\n\nnamespace Diz.Import;\n\npublic abstract class LabelImporter\n{\n    // after import, if there was an error, this will be the line# of what it was.\n    // if -1, we parsed the entire file.\n    public int LastErrorLineNumber { get; private set; } = -1;\n    \n    // we don't modify labels in the open project directly, instead we read them\n    // into here and only return this on success.\n    private readonly Dictionary<int, IAnnotationLabel> newLabels = new(); \n    \n    public virtual Dictionary<int, IAnnotationLabel> ReadLabelsFromFile(string importFilename)\n    {\n        newLabels.Clear();\n        LastErrorLineNumber = 0;\n        \n        var lineIndex = 0;\n        foreach (var line in Util.ReadLines(importFilename))\n        {\n            LastErrorLineNumber = lineIndex + 1;\n            ParseLine(line);\n            lineIndex++;\n        }\n        \n        if (lineIndex == 0)\n            throw new InvalidDataException(\"No lines in file, can't import.\");\n        \n        LastErrorLineNumber = -1;\n        return newLabels;\n    }\n\n    private void ParseLine(string line)\n    {\n        var labelFound = TryParseLabelFromLine(line);\n        if (labelFound == null) \n            return;\n        \n        var (label, labelAddress) = labelFound.Value;\n        TryImportLabel(label, labelAddress);\n    }\n\n    private void TryImportLabel(IAnnotationLabel label, string labelAddress)\n    {\n        var validLabelChars = new Regex(@\"^([a-zA-Z0-9_\\-+\\.\\-]*)$\");\n        if (!validLabelChars.Match(label.Name).Success)\n            throw new InvalidDataException(\"invalid label name: \" + label.Name);\n\n        var address = int.Parse(labelAddress, NumberStyles.HexNumber, null);\n        if (!newLabels.ContainsKey(address))\n        {\n            newLabels.Add(address, label);\n        }\n        else\n        {\n            // Update empty label properties instead of overwriting the entire object\n            // if there are multiple definitions (like from BSNES or handmade CSV)\n            var thisLabel = newLabels[address];\n\n            if (thisLabel.Name.Length > 0)\n                thisLabel.Name = label.Name;\n\n            if (thisLabel.Comment.Length > 0)\n                thisLabel.Comment = label.Comment;\n        }\n    }\n\n    protected abstract (IAnnotationLabel label, string labelAddress)? TryParseLabelFromLine(string line);\n}\n\npublic static class LabelImporterUtils\n{\n    // exception handling/line# stuff needs a little rework, messy.\n    public static void ImportLabelsFromCsv(this ILabelProvider labelProvider, string importFilename, bool replaceAll, bool smartMerge, out int errLine)\n    {\n        // could probably do this part more elegantly\n        errLine = 0;\n        LabelImporter? importer = null;\n        if (BsnesSymbolLabelImporter.IsFileCompatible(importFilename))\n        {\n            importer = new BsnesSymbolLabelImporter();\n        }\n        else if (LabelImporterCsv.IsFileCompatible(importFilename))\n        {\n            importer = new LabelImporterCsv();\n        }\n\n        if (importer == null)\n        {\n            throw new InvalidDataException($\"No importer was found that can import a file named:\\n'{importFilename}'\");\n        }\n\n        var labelsFromFile = importer.ReadLabelsFromFile(importFilename);\n        if (importer.LastErrorLineNumber != -1)\n        {\n            errLine = importer.LastErrorLineNumber;\n            throw new InvalidDataException(\n                $\"Error importing file:\\n'{importFilename}'\\nNear line#: {importer.LastErrorLineNumber}\");\n        }\n\n        if (replaceAll)\n            labelProvider.DeleteAllLabels();\n        \n        labelProvider.AppendLabels(labelsFromFile, smartMerge: smartMerge);\n    }\n}"
  },
  {
    "path": "Diz.Import/src/LabelImporterCsv.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.util;\n\nnamespace Diz.Import;\n\npublic class LabelImporterCsv : LabelImporter\n{\n    public static bool IsFileCompatible(string importFilename) => \n        importFilename.ToLower().EndsWith(\".csv\");\n\n    protected override (IAnnotationLabel label, string labelAddress)? TryParseLabelFromLine(string line)\n    {\n        // TODO: replace with something better. this is kind of a risky/fragile way to parse CSV lines.\n        // it won't deal with weirdness in the comments, quotes, etc.\n        Util.SplitOnFirstComma(line, out var labelAddress, out var remainder);\n        Util.SplitOnFirstComma(remainder, out var labelName, out var labelComment);\n        var label = new Label\n        {\n            Name = labelName.Trim(),\n            Comment = labelComment\n        };\n        return (label, labelAddress);\n    }\n}"
  },
  {
    "path": "Diz.Import/src/ServiceRegistration.cs",
    "content": "﻿using LightInject;\n\nnamespace Diz.Import;\n\npublic class DizImportServiceRegistration : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        \n    }\n}"
  },
  {
    "path": "Diz.Import/src/bizhawk/BizHawkCdlImporter.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Cpu._65816;\n\nnamespace Diz.Import.bizhawk;\n\npublic class BizHawkCdlImporter\n{\n    private readonly Dictionary<string, IList<Flag>> cdl = new();\n        \n    [Flags]\n    public enum Flag : byte\n    {\n        None = 0x00,\n        ExecFirst = 0x01,\n        ExecOperand = 0x02,\n        CpuData = 0x04,\n        DmaData = 0x08,\n        CpuxFlag = 0x10,\n        CpumFlag = 0x20,\n        Brr = 0x80\n    }\n\n    public static void Import(string filename, ISnesData data)\n    {\n        var cdl = new BizHawkCdlImporter();\n        cdl.LoadFromFile(filename);\n        cdl.CopyInto(data);\n    }\n\n    private void LoadFromFile(string path)\n    {\n        using var fs = new FileStream(path, FileMode.Open);\n        LoadFromStream(fs);\n    }\n\n    private void LoadFromStream(Stream input)\n    {\n        var br = new BinaryReader(input);\n\n        string id = br.ReadString();\n        string subType = id switch\n        {\n            \"BIZHAWK-CDL-1\" => \"PCE\",\n            \"BIZHAWK-CDL-2\" => br.ReadString().TrimEnd(' '),\n            _ => throw new InvalidDataException(\"File is not a BizHawk CDL file.\")\n        };\n\n        if (subType != \"SNES\")\n        {\n            throw new InvalidDataException(\"The CDL file is not for SNES.\");\n        }\n\n        int count = br.ReadInt32();\n        for (var i = 0; i < count; i++)\n        {\n            string key = br.ReadString();\n            int len = br.ReadInt32();\n            var data = br.ReadBytes(len).Select(b => (Flag)b).ToArray();\n            cdl[key] = data;\n        }\n    }\n    private void CopyInto(ISnesData snesData)\n    {\n        if (!cdl.TryGetValue(\"CARTROM\", out var cdlRomFlags))\n        {\n            throw new InvalidDataException(\"The CDL file does not contain CARTROM block.\");\n        }\n\n        var size = Math.Min(cdlRomFlags.Count, snesData.GetRomSize());\n        bool m = false;\n        bool x = false;\n        for (var offset = 0; offset < size; offset++)\n        {\n            var cdlFlag = cdlRomFlags[offset];\n            if (cdlFlag == Flag.None)\n                continue;\n\n            if (snesData.GetFlag(offset) != FlagType.Unreached)\n                continue;\n\n            var type = FlagType.Unreached;\n            if ((cdlFlag & Flag.ExecFirst) != 0)\n            {\n                type = FlagType.Opcode;\n                m = (cdlFlag & Flag.CpumFlag) != 0;\n                x = (cdlFlag & Flag.CpuxFlag) != 0;\n            }\n            else if ((cdlFlag & Flag.ExecOperand) != 0)\n                type = FlagType.Operand;\n            else if ((cdlFlag & Flag.CpuData) != 0)\n                type = FlagType.Data8Bit;\n            else if ((cdlFlag & Flag.DmaData) != 0)\n                type = FlagType.Data8Bit;\n                \n            snesData.MarkTypeFlag(offset, type, 1);\n\n            if (type != FlagType.Opcode && type != FlagType.Operand) \n                continue;\n\n            // Operand reuses the last M and X flag values used in Opcode,\n            // since BizHawk CDL records M and X flags only in Opcode.\n            snesData.MarkMFlag(offset, m, 1);\n            snesData.MarkXFlag(offset, x, 1);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/BsnesSymbolLabelImporter.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model;\n\nnamespace Diz.Import.bsnes;\n\n// there's a few different flavors of .sym.cpu files.\n// one is here: https://github.com/BenjaminSchulte/fma-snes65816/blob/master/docs/symbols.adoc\n// another is from the BSNES+ debugger, which is slightly different.  try and support both here if we can, or, split out the parser if needed.\n\npublic class BsnesSymbolLabelImporter : LabelImporter\n{\n    private string currentBsnesSection = \"\";\n\n    public override Dictionary<int, IAnnotationLabel> ReadLabelsFromFile(string importFilename)\n    {\n        currentBsnesSection = \"\";\n        return base.ReadLabelsFromFile(importFilename);\n    }\n\n    public static bool IsFileCompatible(string importFilename)\n    {\n        // Coming in from BSNES symbol map if it begins with the header\n        return importFilename.ToLower().EndsWith(\".cpu.sym\");\n        \n        // here's another way to check if the file contents match.\n        // this signature can be present (but isn't always) present in some BSNES versions (it's not in BSNES+)\n        // the above filename extension check is probably sufficient for all of it though\n        // lines.Length > 0 && lines[0].StartsWith(\"#SNES65816\");\n    }\n\n    protected override (IAnnotationLabel label, string labelAddress)? TryParseLabelFromLine(string line)\n    {\n        if (ShouldSkipLineBecauseCommentOrWhitespace(line)) \n            return null;\n\n        // did we enter a new section? if so, note it, and move to next line\n        if (TryParseBsnesSection(line))\n            return null;\n\n        switch (currentBsnesSection)\n        {\n            case \"[LABELS]\":\n            case \"[SYMBOL]\":\n            {\n                var symbols = line.Trim().Split(' ');\n                var labelAddress = ParseSnesAddress(symbols[0]);\n                var label = new Label\n                {\n                    Name = symbols[1].Replace(\".\", \"_\") // Replace dots which are valid in BSNES\n                };\n                return (label, labelAddress);\n            }\n            case \"[COMMENT]\":\n            {\n                var comments = line.Trim().Split(' ', 2);\n                var labelAddress = ParseSnesAddress(comments[0]);\n                var label = new Label\n                {\n                    Comment = comments[1].Replace(\"\\\"\", \"\") // Remove quotes\n                };\n                return (label, labelAddress);\n            }\n        }\n\n        return null;\n    }\n\n    private bool TryParseBsnesSection(string line)\n    {\n        // BSNES symbol files are multiple INI sections like \"[symbol]\"\n        // we only care about a few of them for Diztinguish\n        // if we hit a section header, consume it, keep going\n        if (!line.StartsWith('[') || !line.EndsWith(']'))\n            return false;\n\n        currentBsnesSection = line.ToUpper();\n        return true;\n    }\n\n    private static string ParseSnesAddress(string symbols) => \n        symbols.Replace(\":\", \"\").ToUpper();\n\n    private static bool ShouldSkipLineBecauseCommentOrWhitespace(string line) => \n        line.Trim().Length == 0 || line.StartsWith('#') || line.StartsWith(';');\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BSNESImportStreamProcessor.cs",
    "content": "﻿// #define PROFILING\n\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing Diz.Core.util;\nusing ICSharpCode.SharpZipLib.Zip.Compression.Streams;\n\nnamespace Diz.Import.bsnes.tracelog;\n\npublic abstract class PoolItem\n{\n    public bool IsFree = true;\n}\n\npublic class ObjPool<T> where T : PoolItem, new()\n{\n    private readonly ConcurrentStack<T> freeObjects;\n\n    public ObjPool()\n    {\n        freeObjects = new ConcurrentStack<T>();\n    }\n\n    public T Get()\n    {\n        var item = Alloc();\n        Debug.Assert(item.IsFree);\n        item.IsFree = false;\n        return item;\n    }\n\n    private T Alloc()\n    {\n        return freeObjects.TryPop(out var item) ? item : new T();\n    }\n\n    public void Return(ref T? item)\n    {\n        if (item == null)\n            return;\n            \n        Debug.Assert(!item.IsFree);\n        item.IsFree = true;\n        DeAlloc(item);\n\n        item = null;\n    }\n\n    private void DeAlloc(T item)\n    {\n        freeObjects.Push(item);\n    }\n}\n\npublic class BsnesImportStreamProcessor\n{\n    public class WorkItemDecompressSnesTraces : PoolItem\n    {\n        // input: a compressed buffer sent to us by BSNES with hundreds of tracelog instructions\n        public const int HeaderSize = 9;\n        public byte[]? Header;                  // 1 byte ID, 4 bytes CompressedBufferSize (which is gzip'd) to follow. 4 bytes: length of uncompressed data \n        public byte[]? CompressedBuffer;\n        public int CompressedSize;\n        \n        // output: a buffer where the compressed buffer input will be extracted to.\n        public byte[]? UncompressedBuffer;\n        public int UncompressedSize;\n        public bool WasDecompressed;\n\n        public byte[]? ScratchBuffer;\n        \n        public readonly List<WorkItemSnesTrace?>? ListHeads = new();\n\n        // copy of the settings as they existed at the moment of capture\n        public BsnesTraceLogCaptureController.TraceLogCaptureSettings CaptureSettings { get; set;  } = new();\n    }\n\n    // represents both a SNES trace item.\n    // performance: this is ALSO stored as a linked list that can be traversed by a worker thread\n    // to act as a work queue.\n    // performance note: we need to process an extremely high volume of these. at this level,\n    // every memory allocation and CPU operation counts.\n    public class WorkItemSnesTrace : PoolItem\n    {\n        public byte[]? Buffer;\n        public bool AbridgedFormat;\n        \n        // copy of the settings as they existed at the moment of original capture\n        public BsnesTraceLogCaptureController.TraceLogCaptureSettings CaptureSettings { get; } = new();\n        \n        // linked list: reference to the next SNES trace that we should process\n        public WorkItemSnesTrace? Next;\n    }\n\n    private ObjPool<WorkItemDecompressSnesTraces>? poolCompressedWorkItems;\n    private ObjPool<WorkItemSnesTrace>? poolWorkItems;\n\n    public CancellationTokenSource CancelToken { get; set; } = new();\n\n    public BsnesImportStreamProcessor(bool poolAllocations = true)\n    {\n        if (!poolAllocations)\n            return;\n\n        poolCompressedWorkItems = new ObjPool<WorkItemDecompressSnesTraces>();\n        poolWorkItems = new ObjPool<WorkItemSnesTrace>();\n    }\n\n    public void Shutdown()\n    {\n        poolWorkItems = null;\n        poolCompressedWorkItems = null;\n\n        // reset for next time around\n        CancelToken = new CancellationTokenSource();\n    }\n\n    public IEnumerable<WorkItemDecompressSnesTraces> GetCompressedWorkItems(Stream? stream)\n    {\n        while (!CancelToken.IsCancellationRequested)\n        {\n            // perf: huge volume of data coming in.\n            // need to get the data read from the server as fast as possible.\n            // do minimal processing here then dump it over to the other thread for real processing.\n            var compressedWorkItem = ReadPacketFromStream(stream);\n            if (compressedWorkItem == null)\n                break;\n            \n            // prob right here, add in the current settings so we capture them.\n            // TODO:\n\n            yield return compressedWorkItem;\n        }\n    }\n\n    private WorkItemDecompressSnesTraces? ReadPacketFromStream(Stream? stream)\n    {\n        #if PROFILING\n        var mainSpan = Markers.EnterSpan(\"BSNES socket read\");\n        #endif\n\n        var item = AllocateCompressedWorkItem();\n        Debug.Assert(item.Header != null);\n\n        try\n        {\n            Util.ReadNext(stream, item.Header, WorkItemDecompressSnesTraces.HeaderSize);\n        }\n        catch (EndOfStreamException)\n        {\n            FreeCompressedWorkItem(ref item);\n            return null;\n        }\n\n        #if PROFILING\n        Markers.WriteFlag(\"initial read\");\n        #endif\n\n        if (item.Header.Length != WorkItemDecompressSnesTraces.HeaderSize)\n            throw new InvalidDataException($\"invalid header length for compressed data chunk\");\n\n        if (item.Header[0] != 'Z')\n            throw new InvalidDataException($\"expected header byte of 'Z', got {item.Header[0]} instead.\");\n\n        item.UncompressedSize = ByteUtil.ConvertByteArrayToInt32(item.Header, 1);\n        item.CompressedSize = ByteUtil.ConvertByteArrayToInt32(item.Header, 5);\n\n        // allocation pool.  if we need to allocate for compressed data, let's go slightly higher so that\n        // we have a chance of re-using this buffer without needing to re-allocate.\n        const float\n            unscientificGuessAtExtraWeShouldAdd = 1.3f; // total guess. adjust higher if you do too many allocations\n        if (item.CompressedBuffer == null || item.CompressedBuffer.Length < item.CompressedSize)\n            item.CompressedBuffer = new byte[(int) (item.CompressedSize * unscientificGuessAtExtraWeShouldAdd)];\n\n        #if PROFILING\n        Markers.WriteFlag(\"big read start\");\n        #endif\n        \n        int bytesRead;\n        try\n        {\n            bytesRead = Util.ReadNext(stream, item.CompressedBuffer, item.CompressedSize);\n        }\n        catch (EndOfStreamException)\n        {\n            FreeCompressedWorkItem(ref item);\n            return null;\n        }\n\n        #if PROFILING\n        Markers.WriteFlag(\"big read done\");\n        #endif\n\n        if (bytesRead != item.CompressedSize)\n        {\n            throw new InvalidDataException(\n                $\"compressed data: expected {item.CompressedSize} bytes, only got {bytesRead}\");\n        }\n\n        #if PROFILING\n        mainSpan.Leave();\n        #endif\n\n        return item;\n    }\n\n    private WorkItemDecompressSnesTraces AllocateCompressedWorkItem()\n    {\n        var item = AllocCompressedWorkItem();\n\n        item.CompressedSize = item.UncompressedSize = 0;\n        item.WasDecompressed = false;\n\n        // did we already allocate this before, and is it the same size?\n        if (item.Header is { Length: WorkItemDecompressSnesTraces.HeaderSize })\n            return item; // we did! optimized path OK\n\n        // un-optimized path: allocate it so we can use it (happens on first use too)\n        item.Header = new byte[WorkItemDecompressSnesTraces.HeaderSize];\n        return item;\n    }\n\n    private WorkItemDecompressSnesTraces AllocCompressedWorkItem()\n    {\n        return poolCompressedWorkItems == null ? new WorkItemDecompressSnesTraces() : poolCompressedWorkItems.Get();\n    }\n\n    private WorkItemSnesTrace AllocWorkItem()\n    {\n        return poolWorkItems == null ? new WorkItemSnesTrace() : poolWorkItems.Get();\n    }\n\n    public void FreeCompressedWorkItem(ref WorkItemDecompressSnesTraces? compressedItem)\n    {\n        // performance: remember: we don't want to kill the big buffers.\n        // main point of this pool is to hopefully re-use them later.\n\n        if (compressedItem == null)\n            return;\n\n        // keep the capacity, but kill the contents.\n        // also, go a little overkill and kill the references to WorkItem list heads inside the List.\n        if (compressedItem.ListHeads != null)\n        {\n            for (var i = 0; i < compressedItem.ListHeads.Count; ++i)\n            {\n                compressedItem.ListHeads[0] = null;\n            }\n\n            compressedItem.ListHeads.Clear();\n        }\n\n        poolCompressedWorkItems?.Return(ref compressedItem);\n    }\n\n    public void FreeWorkItem(ref WorkItemSnesTrace? workItem)\n    {\n        if (workItem == null)\n            return;\n\n        // don't kill the big buffers. main point of this pool is to hopefully re-use them later.\n\n        workItem.Next = null;\n\n        poolWorkItems?.Return(ref workItem);\n    }\n\n    private WorkItemSnesTrace AllocateWorkItem(byte workItemLen)\n    {\n        var workItem = AllocWorkItem();\n\n        // turn this on if you ever think you have a memory allocation issue\n        #if DEBUG_ALLOC_CHECKING\n        if (workItem.isFree || workItem.Next != null)\n        {\n            Debugger.Break();\n        }\n        #endif\n\n        workItem.AbridgedFormat = false;\n\n        // almost every item re-used from the pool should have the correct buffer size already.\n        // this size is usually fixed, so ok to check for exact len.\n        if (workItem.Buffer != null && workItem.Buffer.Length == workItemLen)\n            return workItem;\n\n        // this is what we're trying to avoid re-allocating\n        // (but we will have to allocate on the first use)\n        workItem.Buffer = new byte[workItemLen];\n\n        return workItem;\n    }\n\n    private static void AllocateUncompressedBuffer(WorkItemDecompressSnesTraces workItemDecompressSnesTraces)\n    {\n        Debug.Assert(workItemDecompressSnesTraces.UncompressedSize != 0);\n        // remember, our goal is to reduce the # of allocations done for compressedWorkItem.UncompressedBuffer\n        // CompressedWorkItems are cached as a pool, and if our buffer size is large enough, we can\n        // avoid a re-allocation.\n\n        if (workItemDecompressSnesTraces.UncompressedBuffer != null &&\n            workItemDecompressSnesTraces.UncompressedBuffer.Length >= workItemDecompressSnesTraces.UncompressedSize)\n            return;\n\n        // go a little more than we need to avoid\n        // re-use needing to re-allocate.\n        // works because our uncompressedsize shouldn't change much\n        const float unscientificMultiplierGuess = 1.2f;\n\n        var size = (int) (workItemDecompressSnesTraces.UncompressedSize * unscientificMultiplierGuess);\n        workItemDecompressSnesTraces.UncompressedBuffer = new byte[size];\n    }\n\n    // take CompressedBuffer and un-gzip it into UncompressedBuffer\n    public void DecompressWorkItem(WorkItemDecompressSnesTraces workItemDecompressSnesTraces)\n    {\n        Debug.Assert(!workItemDecompressSnesTraces.WasDecompressed);\n\n        AllocateUncompressedBuffer(workItemDecompressSnesTraces);\n        var decompressedLength = 0;\n        \n        Debug.Assert(workItemDecompressSnesTraces.CompressedBuffer != null);\n        Debug.Assert(workItemDecompressSnesTraces.UncompressedBuffer != null);\n        if (workItemDecompressSnesTraces.CompressedBuffer == null || workItemDecompressSnesTraces.UncompressedBuffer == null)\n            throw new InvalidOperationException(\"failed to allocate memory for work item buffers\");\n\n        using (var memory = new MemoryStream(workItemDecompressSnesTraces.CompressedBuffer))\n        {\n            using var inflater = new InflaterInputStream(memory);\n            decompressedLength = inflater.Read(workItemDecompressSnesTraces.UncompressedBuffer, 0,\n                workItemDecompressSnesTraces.UncompressedSize);\n        }\n\n        workItemDecompressSnesTraces.WasDecompressed = true;\n\n        if (decompressedLength != workItemDecompressSnesTraces.UncompressedSize)\n            throw new InvalidDataException(\"incorrect decompressed data size\");\n\n        // after this function, compressedWorkItem.CompressedBuffer is no longer needed.\n        // but, leave it allocated so future runs can re-use that buffer.\n    }\n\n    public WorkItemSnesTrace ParseSnesTraceWorkItem(Stream stream, byte workItemId, byte workItemLen)\n    {\n        // given 2 bytes already read (in workItemId and workItemLen), read the rest of the SNES trace info from the input stream\n        \n        var workItem = AllocateWorkItem(workItemLen);\n        if (workItem.Buffer == null)\n            throw new InvalidOperationException(\"failed to allocate (or invalid) buffer for WorkItem\");\n\n        if (workItemId != 0xEE && workItemId != 0xEF)\n            throw new InvalidDataException(\"Missing expected watermark from unzipped data\");\n\n        var abridgedFormat = workItemId == 0xEE;\n\n        workItem.AbridgedFormat = abridgedFormat;\n\n        var bytesRead = stream.Read(workItem.Buffer, 0, workItem.Buffer.Length);\n\n        if (bytesRead != workItem.Buffer.Length)\n            throw new InvalidDataException(\"Didn't read enough bytes from unzipped data\");\n\n        return workItem;\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BSNESTraceLogCapture.cs",
    "content": "﻿// #define PROFILING\n\nusing System.Diagnostics;\nusing System.Net;\nusing System.Net.Sockets;\nusing Diz.Core.util;\nusing Diz.Cpu._65816;\n\n#if PROFILING\nusing Microsoft.ConcurrencyVisualizer.Instrumentation;\n#endif\n\nnamespace Diz.Import.bsnes.tracelog;\n\n// TODO: can probably replace this better with Dataflow TPL or newer async/await. investigate\n// Caution: This class is heavily multi-threaded, pay attention to locking/concurrency issues.\npublic class BsnesTraceLogCaptureController\n{\n    public bool Running { get; private set; }\n\n    private readonly ISnesData snesData;\n    private readonly IWorkerTaskManager taskManager;\n    private readonly BsnesImportStreamProcessor streamProcessor;\n    private readonly BsnesTraceLogImporter importer;\n    \n    private int statsBytesToProcess;\n    private int statsCompressedBlocksToProcess;\n    private BsnesTraceLogImporter.Stats cachedStats;\n    \n    public int BlocksToProcess => statsCompressedBlocksToProcess;\n    public bool Finishing => streamProcessor.CancelToken.IsCancellationRequested;\n\n    public BsnesTraceLogCaptureController(ISnesData snesData)\n    {\n        this.snesData = snesData;\n        streamProcessor = new BsnesImportStreamProcessor();\n        \n        // taskManager = new WorkerTaskManagerSynchronous(); // single-threaded version (for testing/debug only)\n        taskManager = new WorkerTaskManager(); // multi-threaded version\n        \n        importer = new BsnesTraceLogImporter(snesData);\n    }\n    \n    public void Run()\n    {\n        try\n        {\n            Running = true;\n            \n            taskManager.Start();\n            Main();\n            taskManager.StartFinishing();\n            taskManager.WaitForAllTasksToComplete();\n        }\n        finally\n        {\n            Shutdown();\n        }\n    }\n\n    private void Shutdown()\n    {\n        streamProcessor.Shutdown();\n        Running = false;\n    }\n\n    private static Stream? GetInputStream() => OpenNetworkStream();\n\n    private static NetworkStream? OpenNetworkStream(IPAddress? ip = null, int port = 27015)\n    {\n        var tcpClient = new TcpClient();\n\n        var remoteIp = ip;\n        if (ip == null)\n        {\n            // weirdly, it seems we can't just use IPAddress.Loopback anymore because it resolves to a weird IP\n            // that doesn't always work.  we'll DNS lookup localhost instead\n            var localhostAddresses = Dns.GetHostAddresses(\"localhost\");\n            if (localhostAddresses.Length > 0)\n            {\n                remoteIp = localhostAddresses[0]; // just pick the first one.\n            }\n        }\n\n        if (remoteIp == null)\n            return null;\n        \n        tcpClient.Connect(remoteIp, port);\n        return tcpClient.GetStream();\n    }\n\n    protected virtual void Main()\n    {\n        #if PROFILING\n        var mainSpan = Markers.EnterSpan(\"BSNES Main\");\n        #endif\n\n        var networkStream = GetInputStream();\n        \n        // process incoming stream data until there's none left or we cancel\n        ProcessStreamData(networkStream);\n\n        // finally, copy any comments generated into snesData\n        importer.CopyTempGeneratedCommentsIntoMainSnesData();\n\n        #if PROFILING\n        mainSpan.Leave();\n        #endif\n    }\n\n    private const int MaxNumCompressedItemsToProcess = -1; // debug only.\n\n    // set a limit for the max# of worker tasks allowed to operate on the compressed data. tweak this number as needed.\n    // this is purely for throttling and not for thread safety, otherwise # of Tasks will run out of control.\n    private readonly SemaphoreSlim compressedWorkersLimit = new(4,4);\n    private readonly SemaphoreSlim uncompressedWorkersLimit = new(4, 4);\n\n    // these can be modified as the trace is happening:\n    public struct TraceLogCaptureSettings\n    {\n        public bool RemoveTracelogLabels { get; set; } = false;\n\n        public bool AddTracelogLabel { get; set; } = false;\n\n        public bool CaptureLabelsOnly { get; set; } = false;\n\n        public string CommentTextToAdd { get; set; } = \"\";\n        \n\n        public TraceLogCaptureSettings()\n        {\n        }\n    }\n\n    public TraceLogCaptureSettings CaptureSettings { get; set; } = new();\n\n    private void ProcessStreamData(Stream? networkStream)\n    {\n        if (streamProcessor == null || taskManager == null)\n            throw new InvalidOperationException(\"stream processor and task manager must not be null\");\n        \n        var count = 0;\n        \n        using var enumWorkItemSnesTraces = streamProcessor.GetCompressedWorkItems(networkStream).GetEnumerator();\n        while (streamProcessor.CancelToken is { IsCancellationRequested: false } && enumWorkItemSnesTraces.MoveNext())\n        {\n            var workItemSnesTraces = enumWorkItemSnesTraces.Current;\n\n            // could put this processing handler inside the task to start the task sooner after we hit this.\n            // doing it here will limit the # of tasks created and waiting, and the # of compressedItems active at once,\n            // which can run away very quickly.\n            \n            // first, let's capture the settings as they were at the TIME OF QUEUEING so when they are processed later,\n            // we'll use these settings even if they've since changed.\n            workItemSnesTraces.CaptureSettings = CaptureSettings;\n            \n            taskManager.Run(() =>\n            {\n                try\n                {\n                    compressedWorkersLimit.Wait(streamProcessor.CancelToken.Token);\n                    try\n                    {\n                        ProcessCompressedSnesTracesWorkItem(workItemSnesTraces);\n                    }\n                    finally\n                    {\n                        compressedWorkersLimit.Release();\n                    }\n                } catch (OperationCanceledException) {\n                    Debug.WriteLine(\"Cancelling...\");\n                    // NOP\n                }\n            });\n            Stats_MarkQueued(workItemSnesTraces);\n\n            count++;\n            if (MaxNumCompressedItemsToProcess != -1 && count >= MaxNumCompressedItemsToProcess)\n                return;\n        }\n        \n        Trace.WriteLine($\"Processed {count} compressed work items.\");\n    }\n\n    private async void ProcessCompressedSnesTracesWorkItem(BsnesImportStreamProcessor.WorkItemDecompressSnesTraces? workItemSnesTraces)\n    {\n        #if PROFILING\n        var mainSpan = Markers.EnterSpan(\"BSNES ProcessCompressedWorkItem\");\n        #endif\n        \n        Debug.Assert(workItemSnesTraces != null);\n        Debug.Assert(streamProcessor != null);\n\n        // 1. take the compressed buffer in this work item and decompress it\n        DecompressSnesBuffers(workItemSnesTraces);\n        \n        // 2. parse the SNES trace data in the newly uncompressed buffer\n        // this uncompressed buffer contains thousands of individual CPU instruction trace data for SNES instructions that BSNES just executed\n        // \n        // we'll divvy up the firehose of data into several queues for processing  \n        CreateSnesTraceWorkQueues(workItemSnesTraces);\n        \n        // with our work queues built, fire off multiple parallel tasks to chew on the trace data.\n        // these threads will parse the trace info and modify the Diz project based on the current tracelog capture settings\n        var subTasks = DispatchWorkersForSnesTraceProcessing(workItemSnesTraces);\n        \n        var statsBytesCompleted = workItemSnesTraces.CompressedSize;\n        \n        // as soon as we've queued things up, we can free and re-use this workitem.\n        // important to return our item to the pool ASAP so it can be re-used and the app doesn't run out of memory\n        streamProcessor.FreeCompressedWorkItem(ref workItemSnesTraces);\n        \n        // wait for all workers to finish chewing on their SNES traces\n        await Task.WhenAll(subTasks);\n\n        // all workers now done\n        Stats_MarkCompleted(statsBytesCompleted);\n\n        #if PROFILING\n        mainSpan.Leave();\n        #endif\n    }\n\n    private IEnumerable<Task> DispatchWorkersForSnesTraceProcessing(BsnesImportStreamProcessor.WorkItemDecompressSnesTraces itemDecompressSnesTraces)\n    {\n        if (itemDecompressSnesTraces.ListHeads == null)\n            throw new InvalidDataException(\"Expected non-null ListHeads for compressed work item dispatch\");\n        \n        Debug.Assert(streamProcessor != null);\n\n        // make a COPY of this struct so threads get the copy\n        var captureSettings = itemDecompressSnesTraces.CaptureSettings;\n        \n        var subTasks = new List<Task>(capacity: itemDecompressSnesTraces.ListHeads.Count);\n        for (var i = 0; i < itemDecompressSnesTraces.ListHeads.Count; ++i)\n        {\n            var workItemListHead = itemDecompressSnesTraces.ListHeads[i];\n            if (workItemListHead == null)\n                continue;\n            \n            subTasks.Add(taskManager.Run(() =>\n            {\n                // important: avoid passing the worker thread any references to itemDecompressSnesTraces here.\n                // we want to be fully separated from that so as soon as this task starts,\n                // we can immediately re-use itemDecompressSnesTraces\n                try\n                {\n                    uncompressedWorkersLimit.Wait(streamProcessor.CancelToken.Token);\n                    try\n                    {\n                        ProcessWorkItemsLinkedList(workItemListHead, captureSettings);\n                    }\n                    finally\n                    {\n                        uncompressedWorkersLimit.Release();\n                    }\n                }\n                catch (OperationCanceledException)\n                {\n                    Debug.WriteLine(\"Cancelling2...\");\n                    // NOP\n                }\n            }));\n\n            itemDecompressSnesTraces.ListHeads[i] = null; // remove the reference.\n        }\n\n        return subTasks;\n    }\n\n    private void CreateSnesTraceWorkQueues(BsnesImportStreamProcessor.WorkItemDecompressSnesTraces workItemSnesTraces)\n    {\n        Debug.Assert(workItemSnesTraces.WasDecompressed);\n        Debug.Assert(workItemSnesTraces.UncompressedBuffer != null);\n        \n        using var stream = new MemoryStream(workItemSnesTraces.UncompressedBuffer, 0, workItemSnesTraces.UncompressedSize);\n        workItemSnesTraces.ScratchBuffer ??= new byte[2];\n        \n        // tune this as needed.\n        // we want parallel jobs going, but, we don't want too many of them at once.\n        // average # workItems per CompressedWorkItem is like 12K currently.\n        const int numItemsPerTask = 6000;\n        bool keepGoing;\n        var itemsRemainingBeforeEnd = numItemsPerTask;\n\n        Debug.Assert(workItemSnesTraces.ListHeads != null && workItemSnesTraces.ListHeads.Count == 0);\n        \n        BsnesImportStreamProcessor.WorkItemSnesTrace? currentHead = null;\n        BsnesImportStreamProcessor.WorkItemSnesTrace? currentItem = null;\n\n        // read all the SNES traces from the now-uncompressed buffer\n        do\n        {\n            var nextItem = ParseNextSnesTrace(stream, workItemSnesTraces.ScratchBuffer);\n\n            if (nextItem != null)\n            {\n                Debug.Assert(nextItem.Next == null);\n\n                if (currentHead == null)\n                {\n                    currentHead = nextItem;\n                    Debug.Assert(currentItem == null);\n                }\n                else\n                {\n                    Debug.Assert(currentItem != null);\n                    currentItem.Next = nextItem;\n                }\n                currentItem = nextItem;\n\n                itemsRemainingBeforeEnd--;\n            }\n            \n            keepGoing = streamProcessor.CancelToken.IsCancellationRequested == false && nextItem != null;\n            var endOfPartition = !keepGoing || itemsRemainingBeforeEnd == 0;\n            if (!endOfPartition)\n                continue;\n\n            // finish list\n            if (currentHead != null)\n            {\n                Debug.Assert(currentItem is { Next: null });\n                workItemSnesTraces.ListHeads.Add(currentHead);\n            }\n            \n            // reset list\n            currentHead = currentItem = null;\n            itemsRemainingBeforeEnd = numItemsPerTask;\n        } while (keepGoing);\n    }\n\n    private BsnesImportStreamProcessor.WorkItemSnesTrace? ParseNextSnesTrace(Stream stream, byte[] header)\n    {\n        if (stream.Read(header, 0, 2) != 2) \n            return null;\n        \n        var workItemId = header[0];\n        var workItemLen = header[1];\n        return streamProcessor.ParseSnesTraceWorkItem(stream, workItemId, workItemLen);\n    }\n\n    private void DecompressSnesBuffers(BsnesImportStreamProcessor.WorkItemDecompressSnesTraces itemDecompressSnesTraces)\n    {\n        Debug.Assert(itemDecompressSnesTraces.CompressedBuffer != null);\n        Debug.Assert(itemDecompressSnesTraces.UncompressedSize != 0);\n        Debug.Assert(itemDecompressSnesTraces.UncompressedSize != 0);\n        Debug.Assert(!itemDecompressSnesTraces.WasDecompressed);\n        \n        streamProcessor.DecompressWorkItem(itemDecompressSnesTraces);\n        \n        Debug.Assert(itemDecompressSnesTraces.UncompressedBuffer != null);\n        Debug.Assert(itemDecompressSnesTraces.WasDecompressed);\n    }\n\n    private void ProcessWorkItemsLinkedList(BsnesImportStreamProcessor.WorkItemSnesTrace workItemSnesTrace, in TraceLogCaptureSettings captureSettings)\n    {\n        // performance critical function. be cautious when making changes\n        \n        #if PROFILING\n        var mainSpan = Markers.EnterSpan(\"BSNES ProcessWorkItems\");\n        #endif\n\n        // iterate linked list\n        var current = workItemSnesTrace;\n        while (current != null) { \n            ProcessWorkItemSnesTrace(current, in captureSettings);\n            var next = current.Next;\n            streamProcessor.FreeWorkItem(ref current);\n            current = next;\n        }\n        \n        #if PROFILING\n        mainSpan.Leave();\n        #endif\n    }\n\n    // this is just neat stats. it's optional, remove if performance becomes an issue (seems unlikely)\n    private void Stats_MarkQueued(BsnesImportStreamProcessor.WorkItemDecompressSnesTraces itemDecompressSnesTraces)\n    {\n        Interlocked.Add(ref statsBytesToProcess, itemDecompressSnesTraces.CompressedSize);\n        Interlocked.Increment(ref statsCompressedBlocksToProcess);\n    }\n\n    private void Stats_MarkCompleted(int bytesCompleted)\n    {\n        Interlocked.Add(ref statsBytesToProcess, -bytesCompleted);\n        Interlocked.Decrement(ref statsCompressedBlocksToProcess);\n    }\n\n    private void ProcessWorkItemSnesTrace(BsnesImportStreamProcessor.WorkItemSnesTrace workItemSnesTrace, in TraceLogCaptureSettings captureSettings)\n    {\n        #if PROFILING\n        var mainSpan = Markers.EnterSpan(\"BSNES ProcessWorkItem\");\n        #endif\n\n        if (workItemSnesTrace.Buffer != null) // should always be non-null but\n        {\n            // this importer call is thread-safe, so we don't need to do our own locking\n            // also, CaptureSettings will be passed by value and copied, so it's thread-safe.\n            importer.ImportTraceLogLineBinary(workItemSnesTrace.Buffer, workItemSnesTrace.AbridgedFormat, captureSettings);\n        }\n\n        #if PROFILING\n        mainSpan.Leave();\n        #endif\n    }\n\n    public void SignalToStop()\n    {\n        streamProcessor.CancelToken.Cancel();\n        taskManager.StartFinishing();\n    }\n\n    public (BsnesTraceLogImporter.Stats stats, int bytesToProcess) GetStats()\n    {\n        if (!Running)\n            return (cachedStats, 0);\n\n        // this is thread-safe and will make a copy for us.\n        cachedStats = importer.CurrentStats;\n\n        return (cachedStats, statsBytesToProcess);\n    }\n}\n\n// mostly for testing, though we could use it to speed up importing too.\n// public class BsnesTraceLogFileCapture : BsnesTraceLogCapture\n// {\n//     private readonly byte[] bytes;\n//\n//     public BsnesTraceLogFileCapture(string dataFile, ISnesData data) : base(data)\n//     {\n//         bytes = File.ReadAllBytes(dataFile);\n//     }\n//\n//     protected override Stream GetInputStream()\n//     {\n//         return new MemoryStream(bytes);\n//     }\n// }\n\n// // debug only. same as above, but, run it a bunch of times mostly for performance\n// // testing and memory allocation reasons.\n// public class BsnesTraceLogDebugBenchmarkFileCapture : BsnesTraceLogFileCapture\n// {\n//     private readonly int numTimes = 1;\n//\n//     // for benchmarking, these are called immediate before and after Main(), which represents the bulk of the CPU work.\n//     // if optimizing, making these work faster is a good place to start.\n//     public Action OnStart { get; set; }\n//     public Action OnStop { get; set; }\n//     \n//     protected override void Main()\n//     {\n//         OnStart?.Invoke();\n//         \n//         for (var i = 0; i < numTimes; ++i)\n//             base.Main();\n//\n//         OnStop?.Invoke();\n//     }\n//\n//     public BsnesTraceLogDebugBenchmarkFileCapture(string dataFile, int numTimes) : base(dataFile)\n//     {\n//         this.numTimes = numTimes;\n//     }\n// }"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BSNESTraceLogImporter.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing Diz.Core.Interfaces;\nusing Diz.Cpu._65816;\n\nnamespace Diz.Import.bsnes.tracelog;\n\npublic partial class BsnesTraceLogImporter\n{\n    private readonly ISnesData? snesData = null;\n\n    // note: we could add directly to snesData.Data.Comments BUT locking is too slow on a normal dict,\n    // and updating the rest of the app for ConcurrentDictionary is a bit painful at the moment.\n    // so, we'll buffer any generated comments here in this dict, and at the end we'll copy everything here into \n    // its final home in snesData.Data.Comments\n    private ConcurrentDictionary<int, string> tracelogCommentsGenerated = new();\n\n    // these are cached mostly to save us from having to lock Data.\n    // do not allow these to change over the life of the importer\n    private readonly int romSizeCached;\n    private readonly RomMapMode romMapModeCached;\n\n    public BsnesTraceLogImporter(ISnesData? snesData)\n    {\n        this.snesData = snesData;\n        romSizeCached = this.snesData?.GetRomSize() ?? 0;\n        romMapModeCached = this.snesData?.RomMapMode ?? default;\n\n        modificationDataPool = new ObjPool<ModificationData>();\n        InitStats();\n    }\n\n    // Mark collected trace data for a RomByte (which should be an opcode) AND any of the operands that follow us.\n    // note: snes address provided SHOULD always be an opcode (not operand or data).\n    // it may not always be a valid rom address though (it could be running from RAM/etc)\n    private void ConsumeAndFreeTraceData(ref ModificationData? modData, int instructionByteLen = -1)\n    {\n        // WARNING: extremely performance-intensive function. be really careful when changing stuff.\n        // please profile any changes made here. the slightest thing can break the concurrency and render\n        // the entire tracelog capture system useless.  use DotTrace or similar tools for profiling.\n        \n        if (modData == null)\n            return;\n        \n        // ----------------\n        // part 1: comments (optional but useful side feature)\n        // ----------------\n        // NOTE: you can't count on the stuff in snesData to be updated yet.\n        // when we get here, this function is called on an opcode [instruction] BSNES has given us,\n        // and below will handle marking it as opcodes vs operands.\n        UpdateTracelogComments(modData.SnesAddress, in modData.CaptureSettings);\n        \n        // ----------------\n        // part 2: (the main important thing)\n        // mark flags in Diz project as opcode vs operand (and record MX flags, D, BD, etc)\n        // ----------------\n        var currentOffset = 0;\n        var numBytesAnalyzed = 0;\n\n        // we start at position = 0 as an opcode.\n        // we then traverse the length of this opcode \n        while (true)\n        {\n            // prep\n            numBytesAnalyzed++;\n            \n            // rejects any non-ROM based addresses (like stuff running in RAM)\n            modData.Pc = ConvertSnesToPc(modData.SnesAddress);\n            if (!IsOkToSetThisRomByte(modData.Pc, instructionByteLen, currentOffset))\n                break;\n\n            // finally, modify Diz project: update MX flags and opcode/operand status\n            modData.FlagType = GetFlagForInstructionPosition(currentOffset);\n            if (!modData.CaptureSettings.CaptureLabelsOnly)\n            {\n                var romByte = snesData!.Data.RomBytes[modData.Pc];\n                modData.ApplyModificationIfNeeded(romByte);\n                UpdateStats(modData);\n            }\n\n            // prep for processing followup bytes (operands)\n            modData.SnesAddress = GetNextSNESAddress(modData.SnesAddress);\n            currentOffset++;\n        }\n\n        FreeModificationData(ref modData); // sets this to Null after called\n\n        currentStats.NumRomBytesAnalyzed += numBytesAnalyzed;\n    }\n    \n    // assumption: SNES address is an OPCODE that BSNES has identified (it may not be marked in our project yet though).\n    // do NOT count on any data (Flags i.e. MX, and whether this is an opcode vs operand vs data) yet being set for this\n    // opcode in the Diz project. that may happen AFTER this function is called.\n    //\n    // WARNING: Snes address can be ANYTHING including instructions executing in RAM. it may not map to a ROM address.\n    private void UpdateTracelogComments(int snesAddress, in BsnesTraceLogCaptureController.TraceLogCaptureSettings traceLogCaptureSettings)\n    {\n        string? commentText = null;\n        if (traceLogCaptureSettings is { AddTracelogLabel: true, CommentTextToAdd.Length: > 0 })\n        {\n            commentText = \"TLC:\" + traceLogCaptureSettings.CommentTextToAdd;\n        } \n        else if (traceLogCaptureSettings.RemoveTracelogLabels)\n        {\n            commentText = \"XX*-remove-*XX\";\n        }\n        \n        // if adding OR removing, we need to add something to the comment list.\n        // later, we'll take the entries from this temp list and put them in the Diz project.\n        // we could do it here directly, but, it's too slow.\n        if (commentText != null)\n            tracelogCommentsGenerated.AddOrUpdate(snesAddress, commentText, (_, _) => commentText);\n    }\n    // private void UpdateTracelogComments(int snesAddress, in BsnesTraceLogCapture.TraceLogCaptureSettings traceLogCaptureSettings)\n    // {\n    //     tracelogCommentsGenerated.TryGetValue(snesAddress, out var comment);\n    //\n    //     // 2 things here: \n    //     //   1. if requested, remove any TLC (\"Trace Log Capture\") comments \n    //     var shouldRemoveExisting = traceLogCaptureSettings.RemoveTracelogLabels && comment != null && comment.StartsWith(\"TLC:\");\n    //\n    //     //   2. if requested, add or replace any TLC comments\n    //     var shouldAdd = traceLogCaptureSettings is { AddTracelogLabel: true, CommentTextToAdd.Length: > 0 };\n    //\n    //     // are we allowed to replace an existing comment?\n    //     if (comment != null && shouldAdd)\n    //     {\n    //         if (!comment.StartsWith(\"TLC:\"))\n    //             shouldAdd = false; // no. can't overwrite a non-tracelog comment\n    //         else\n    //             shouldRemoveExisting = true; // fine, but we'll have to remove the existing first\n    //     }\n    //\n    //     switch (shouldRemoveExisting)\n    //     {\n    //         // do we actually need to do anything?\n    //         case false when !shouldAdd:\n    //             return;\n    //         case true:\n    //             tracelogCommentsGenerated.TryRemove(snesAddress, out _);\n    //             break;\n    //     }\n    //\n    //     if (!shouldAdd) \n    //         return;\n    //     \n    //     var newComment = \"TLC:\" + traceLogCaptureSettings.CommentTextToAdd;\n    //     tracelogCommentsGenerated.AddOrUpdate(snesAddress, newComment, (_, _) => newComment);\n    // }\n\n    private bool IsOkToSetThisRomByte(int pc, int instructionByteLen, int opIndex)\n    {\n        if (pc < 0 || pc >= romSizeCached)\n            return false;\n\n        ValidateInstructionByteLen(instructionByteLen);\n        if (opIndex is < 0 or > 4) // using *4* here, not the real SNES max possible of 3.  calling code will deal with invalid '4' case\n        {\n            throw new InvalidDataException($\"Invalid opcode index {opIndex}. Must be between 0 and 4\");\n        }\n\n        if (instructionByteLen != -1)\n        {\n            // normal case: just make sure we're in range if BSNES has told us the amount of bytes we need to process\n            return opIndex < instructionByteLen;\n        }\n        // OTHERWISE: (weirder case) we haven't been told how many bytes to process, so have to do some fuzzy guessing now. play it safe.\n\n        // easy case: this is the first byte, this will be the Opcode, so clear that for takeoff.\n        if (opIndex == 0)\n            return true;\n\n        // otherwise, this is NOT the first byte (Opcode), and we don't have information about how many bytes\n        // past us are Operands. Could be none, could be up to 3.\n        //\n        // We're trying to mark as many bytes as operands with the flags from the tracelog.\n        // We can't safely know though, so, unless they've ALREADY been marked as Operands, let's\n        // just play it safe and stop at the first thing that's NOT an Operand.\n        //\n        // Calling code should ideally not let us get to here, and instead supply us with a valid instructionByteLen\n        return GetFlag(pc) == FlagType.Operand;\n    }\n\n    private FlagType GetFlag(int pc)\n    {\n        snesData!.Data.RomBytes[pc].Lock.EnterReadLock();\n        try\n        {\n            return snesData.GetFlag(pc);\n        }\n        finally\n        {\n            snesData.Data.RomBytes[pc].Lock.ExitReadLock();\n        }\n    }\n\n    private static void ValidateInstructionByteLen(int instructionByteLen)\n    {\n        if (instructionByteLen != -1 && instructionByteLen is < 1 or > 4)\n        {\n            throw new InvalidDataException(\n                $\"Invalid opcode+operand byte length {instructionByteLen}. Must be -1, or between 1 and 4\");\n        }\n    }\n\n    public void CopyTempGeneratedCommentsIntoMainSnesData()\n    {\n        // final step: copy temp generated keys back into snesData.\n        // this is done using a temporary dictionary only for performance reasons (locking is too slow to keep up with the SNES without\n        // using ConcurrentDictionary)\n\n        if (snesData == null)\n            return;\n        \n        // we will allow overwriting existing data in this.snesData.Data.Comments ONLY IF the destination existing kvp values \n        // start with the string \"TLC\". otherwise, we won't allow overwriting.\n        foreach (var tlcGenerateComment in tracelogCommentsGenerated)\n        {\n            var commentAlreadyExists = snesData.Data.Comments.TryGetValue(tlcGenerateComment.Key, out var val);\n            \n            // we're only allowed to modify tracelog comments\n            if (commentAlreadyExists && val != null && !val.StartsWith(\"TLC:\"))\n                continue;\n            \n            // if we get here either there's NO comment already existing, or, it's a tracelog comment and we can overwrite it\n            \n            // REMOVE: if this SNES address is marked for removal... \n            if (tlcGenerateComment.Value == \"XX*-remove-*XX\")\n            {\n                if (snesData.Data.Comments.ContainsKey(tlcGenerateComment.Key))\n                    snesData.Data.Comments.Remove(tlcGenerateComment.Key);\n                \n                continue;\n            }\n            \n            // ADD: add this tracelog value and overwrite anything there\n            // (This adds the key if it doesn't exist, updates if it already does exist)\n            snesData.Data.Comments[tlcGenerateComment.Key] = tlcGenerateComment.Value;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BsnesTraceLogImporter.Caching.cs",
    "content": "﻿namespace Diz.Import.bsnes.tracelog;\n\npublic partial class BsnesTraceLogImporter\n{\n    // PERFORMANCE\n    // this code exists ONLY for performance optimization.\n    //\n    // Turns out, using string.IndexOf() to find relative positions of text is SUPER SLOW.\n    // Tracelogs from even a few seconds of SNES runtime can produce something like\n    // 250 MILLION lines of text.\n    //\n    // Optimization: since the BSNES tracelogs contain lines that are always the same width with the data always\n    // starting on the same offset. so, what we can take a known sample line, and then parse it ONE TIME.\n    // we'll then save the position of each section of data\n    //\n    // This is basically the fastest way to parse text tracelogs (80 bytes per line). HOWEVER, if you really\n    // want to move fast, you should use a binary file format (for BSNES, it's just 8 bytes per instruction,\n    // and no text parsing required) \n    //\n    // NOTE: we will make the assumption that lines in a file will always be the same width and offsets.\n    // however, different versions of BSNES can output different line lengths, so always re-parse the first line\n    // on each use of this file. it's up to the caller to figure that out.\n\n    public class CachedTraceLineTextIndex\n    {\n        // index of the start of the info\n        public int\n            Addr,\n            D,\n            Db,\n            Flags,\n            FN,\n            FV,\n            FM,\n            FX,\n            FD,\n            FI,\n            FZ,\n            FC;\n\n        public int LastLineLength { get; set; } = -1;\n\n        public void RecomputeCachedIndicesBasedOn(string templateLine)\n        {\n            LastLineLength = templateLine.Length;\n\n            int GetIndexOfDataAfterToken(string token)\n            {\n                return templateLine.IndexOf(token, StringComparison.Ordinal) + token.Length;\n            }\n\n            Addr = 0;\n            D = GetIndexOfDataAfterToken(\"D:\");\n            Db = GetIndexOfDataAfterToken(\"DB:\");\n            Flags = Db + 3;\n\n            // flags: nvmxdizc\n            FN = Flags + 0;\n            FV = Flags + 1;\n            FM = Flags + 2;\n            FX = Flags + 3;\n            FD = Flags + 4;\n            FI = Flags + 5;\n            FZ = Flags + 6;\n            FC = Flags + 7;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BsnesTraceLogImporter.ModificationsList.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model;\n\nnamespace Diz.Import.bsnes.tracelog;\n\npublic partial class BsnesTraceLogImporter\n{\n    // important note: Tracelog capture doesn't guarantee ordering for tracelog data received.\n    //\n    // i.e. tracelog worker threads divide and conquer and don't call us in any particular order, \n    // so we might overwrite newer data in an address with older data.\n    //\n    // for tracing applications, I don't think it matters, just keep it in mind. or,\n    // add some support for sequencing of incoming data to keep it in order if it's important.\n\n    public class ModificationData : PoolItem\n    {\n        // imported data from trace log\n        public int SnesAddress;\n        public int Pc;\n        public FlagType FlagType;\n        public int DataBank;\n        public int DirectPage;\n        public bool XFlagSet;\n        public bool MFlagSet;\n\n        // we will set these if any of the above field were modified\n        public bool changed;\n        public bool mDb, mMarks, mDp, mX, mM;\n\n        public BsnesTraceLogCaptureController.TraceLogCaptureSettings CaptureSettings;\n\n        // precondition: rombyte (minimum of) read lock already acquired\n        private void CompareToExisting(ISnesRomByte romByte)\n        {\n            mDb = romByte.DataBank != DataBank;\n            mMarks = romByte.TypeFlag != FlagType;\n            mDp = romByte.DirectPage != DirectPage;\n            mX = romByte.XFlag != XFlagSet;\n            mM = romByte.MFlag != MFlagSet;\n\n            changed = mMarks || mDb || mDp || mX || mM;\n        }\n\n        // precondition: rombyte (minimum of) read lock already acquired\n        private void ApplyModification(IRomByte romByte)\n        {\n            romByte.Lock.EnterWriteLock();\n            try\n            {\n                romByte.TypeFlag = FlagType;\n                romByte.DataBank = (byte) DataBank;\n                romByte.DirectPage = 0xFFFF & DirectPage;\n                romByte.XFlag = XFlagSet;\n                romByte.MFlag = MFlagSet;\n            }\n            finally\n            {\n                romByte.Lock.ExitWriteLock();\n            }\n        }\n\n        public void ApplyModificationIfNeeded(IRomByte romByte)\n        {\n            romByte.Lock.EnterUpgradeableReadLock();\n            try\n            {\n                CompareToExisting(romByte);\n                if (changed)\n                    ApplyModification(romByte);\n            }\n            finally\n            {\n                romByte.Lock.ExitUpgradeableReadLock();\n            }\n        }\n    }\n\n    // optimization: save allocations\n    private readonly ObjPool<ModificationData> modificationDataPool;\n\n    private void UpdateStats(ModificationData modData)\n    {\n        if (!modData.changed)\n            return;\n\n        statsLock.EnterWriteLock();\n        try\n        {\n            currentStats.NumRomBytesModified++;\n\n            currentStats.NumMarksModified += modData.mMarks ? 1 : 0;\n            currentStats.NumDbModified += modData.mDb ? 1 : 0;\n            currentStats.NumDpModified += modData.mDp ? 1 : 0;\n            currentStats.NumXFlagsModified += modData.mX ? 1 : 0;\n            currentStats.NumMFlagsModified += modData.mM ? 1 : 0;\n\n            currentStats.NumCommentsMarked = tracelogCommentsGenerated.Count;\n        }\n        finally\n        {\n            statsLock.ExitWriteLock();\n        }\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BsnesTraceLogImporter.Parsers.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Core.util;\n\nnamespace Diz.Import.bsnes.tracelog;\n\npublic partial class BsnesTraceLogImporter\n{\n    private CachedTraceLineTextIndex TextImportFormatCached { get; } = new();\n    \n    public ModificationData AllocateModificationData()\n    {\n        var modData = modificationDataPool.Get();\n            \n        modData.changed = false;\n            \n        return modData;\n    }\n\n    private void FreeModificationData(ref ModificationData? modData)\n    {\n        if (modData == null)\n            return;\n\n        modificationDataPool.Return(ref modData);\n    }\n        \n    public void ImportTraceLogLine(string line)\n    {\n        var modData = AllocateModificationData();\n        if (!ParseTextLine(line, modData))\n            return;\n\n        UpdatePCAddress(modData);\n        ConsumeAndFreeTraceData(ref modData); // note: frees modData, sets it to null. can't use after this call\n    }\n        \n    // PERFORMANCE CRITICAL FUNCTION\n    // WARNING: THREAD SAFETY: this function will be called from multiple threads concurrently and MUST REMAIN thread-safe.\n    // and, for performance-reasons, we're handling our own locking.\n    public void ImportTraceLogLineBinary(byte[] bytes, bool abridgedFormat, BsnesTraceLogCaptureController.TraceLogCaptureSettings settings)\n    {\n        var modData = AllocateModificationData();\n        \n        // first, make a copy of all settings.\n        modData.CaptureSettings = settings;\n        \n        // then parse the BSNES data for this opcode+operands, and process it\n        ParseBinary(bytes, abridgedFormat, out var opcodeLen, modData);\n        UpdatePCAddress(modData);\n        \n        // note: frees modData and returns to the pool. don't use modData after this call.\n        ConsumeAndFreeTraceData(ref modData, opcodeLen);\n    }\n\n    public bool ParseTextLine(string line, ModificationData modData)\n    {\n        // caution: very performance-sensitive function, please take care when making modifications\n        // string.IndexOf() is super-slow too.\n        // Input lines must follow this strict format and be this exact formatting and column indices.\n        // 028cde rep #$30               A:0004 X:0000 Y:0004 S:1fdd D:0000 DB:02 nvmxdiZC V:133 H: 654 F:36\n        if (line.Length < 80)\n            return false;\n            \n        // performance: we could just parse the whitespace, but, \n        // tracelogs have a huge amount of lines. so we parse the first line,\n        // then save the offsets for all other lines in the same file.\n        if (TextImportFormatCached.LastLineLength != line.Length)\n            TextImportFormatCached.RecomputeCachedIndicesBasedOn(line);\n\n        // TODO: add error treatment / validation here.\n\n        modData.SnesAddress = (int)ByteUtil.ByteParseHex(line, 0, 6);\n        modData.DirectPage = (int)ByteUtil.ByteParseHex(line, TextImportFormatCached.D, 4);\n        modData.DataBank = (int)ByteUtil.ByteParseHex(line, TextImportFormatCached.Db, 2);\n            \n        // 'X' (if emulation mode) or 'B' (if native mode) = unchecked in bsnesplus debugger UI = (8bit)\n        // 'x' or '.' = checked (16bit)\n        modData.XFlagSet = line[TextImportFormatCached.FX] == 'X' || line[TextImportFormatCached.FX] == 'B';\n\n        // 'M' (if emulation mode) or '1' (if native mode) = unchecked in bsnesplus debugger UI = (8bit)\n        // 'm' or '.' = checked (16bit)\n        modData.MFlagSet = line[TextImportFormatCached.FM] == 'M' || line[TextImportFormatCached.FM] == '1';\n            \n        // TODO: we could capture native vs emulation mode here and mark that.\n\n        return true;\n    }\n\n    private static void ParseBinary(byte[] bytes, bool abridgedFormat, out byte opcodeLen, ModificationData modData)\n    {\n        // file format info from the BSNES side:\n        // https://github.com/binary1230/bsnes-plus/blob/e30dfc784f3c40c0db0a09124db4ec83189c575c/bsnes/snes/cpu/core/disassembler/disassembler.cpp#L224\n            \n        // extremely performance-intensive function. be really careful when adding stuff\n        if (abridgedFormat)\n        {\n            if (bytes.Length != 8)\n                throw new InvalidDataException(\"Non-abridged trace data length must be 8 bytes\");\n        }\n        else\n        {\n            if (bytes.Length != 21)\n                throw new InvalidDataException(\"Non-abridged trace data length must be 21 bytes\");\n        }\n\n        var currentIndex = 0;\n\n        // -----------------------------\n        modData.SnesAddress = ByteUtil.ByteArrayToInt24(bytes, currentIndex);\n        currentIndex += 3;\n\n        opcodeLen = bytes[currentIndex++];\n\n        modData.DirectPage = ByteUtil.ByteArrayToInt16(bytes, currentIndex);\n        currentIndex += 2;\n\n        modData.DataBank = bytes[currentIndex++];\n\n        // the flags register\n        var flags = bytes[currentIndex++];\n        // n = flags & 0x80;\n        // v = flags & 0x40;\n        // m = flags & 0x20;\n        // d = flags & 0x08;\n        // i = flags & 0x04;\n        // z = flags & 0x02;\n        // c = flags & 0x01;\n\n        // we only care about X and M flags\n        modData.XFlagSet = (flags & 0x10) != 0;\n        modData.MFlagSet = (flags & 0x20) != 0;\n\n        if (!abridgedFormat)\n        {\n            // skip opcodes. NOTE: must read all 5 bytes but only use up to 'opcode_len' bytes \n            //var op  = bytes[currentIndex++];\n            //var op0 = bytes[currentIndex++];\n            //var op1 = bytes[currentIndex++];\n            //var op2 = bytes[currentIndex++];\n            currentIndex += 4;\n\n            // skip A register\n            currentIndex += 2;\n\n            // skip X register\n            currentIndex += 2;\n\n            // skip Y register\n            currentIndex += 2;\n\n            // skip S register\n            currentIndex += 2;\n\n            // skip, flag 'e' for emulation mode or not\n            // skip E(emu) flag <-- NOTE: we might... want this someday.\n            currentIndex += 1;\n        }\n\n        Debug.Assert(currentIndex == bytes.Length);\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BsnesTraceLogImporter.Stats.cs",
    "content": "﻿namespace Diz.Import.bsnes.tracelog;\n\npublic partial class BsnesTraceLogImporter\n{\n    public struct Stats\n    {\n        public long NumRomBytesAnalyzed;\n        public long NumRomBytesModified;\n\n        public long NumXFlagsModified;\n        public long NumMFlagsModified;\n        public long NumDbModified;\n        public long NumDpModified;\n        public long NumMarksModified;\n        public int NumCommentsMarked;\n    }\n\n    private readonly ReaderWriterLockSlim statsLock = new();\n    private Stats currentStats;\n\n    // return a copy of struct so caller doesn't have to deal with thread safety issues \n    public Stats CurrentStats {\n        get {\n            statsLock.EnterReadLock();\n            try {\n                return currentStats;\n            } finally {\n                statsLock.ExitReadLock();\n            }\n        }\n    }\n\n    private void InitStats()\n    {\n        statsLock.EnterWriteLock();\n        try {\n            currentStats.NumRomBytesAnalyzed = 0;\n            currentStats.NumRomBytesModified = 0;\n            currentStats.NumXFlagsModified = 0;\n            currentStats.NumMFlagsModified = 0;\n            currentStats.NumDbModified = 0;\n            currentStats.NumDpModified = 0;\n            currentStats.NumMarksModified = 0;   \n        } finally {\n            statsLock.ExitWriteLock();\n        }\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/tracelog/BsnesTraceLogImporter.Util.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.util;\n\nnamespace Diz.Import.bsnes.tracelog;\n\npublic partial class BsnesTraceLogImporter\n{\n    private int ConvertSnesToPc(int modDataSnesAddress)\n    {\n        // PERF: could use Data.ConvertSnesToPc(), but, by caching the two variables here,\n        // we can save some locking and maybe speed things up.\n        return RomUtil.ConvertSnesToPc(modDataSnesAddress, romMapModeCached, romSizeCached);\n    }\n\n    private static int GetNextSNESAddress(int modDataSnesAddress)\n    {\n        return RomUtil.CalculateSnesOffsetWithWrap(modDataSnesAddress, 1);\n    }\n\n    private static FlagType GetFlagForInstructionPosition(int currentIndex)\n    {\n        return currentIndex == 0 ? FlagType.Opcode : FlagType.Operand;\n    }\n\n    private void UpdatePCAddress(ModificationData modData)\n    {\n        modData.Pc = ConvertSnesToPc(modData.SnesAddress);\n    }\n}"
  },
  {
    "path": "Diz.Import/src/bsnes/usagemap/BSNESUsageMapImporter.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Cpu._65816;\n\nnamespace Diz.Import.bsnes.usagemap;\n\npublic class BsnesUsageMapImporter\n{\n    [Flags]\n    private enum BsnesPlusUsage : byte\n    {\n        UsageRead   = 0x80,\n        UsageWrite  = 0x40,\n        UsageExec   = 0x20,\n        \n        UsageOpcode = 0x10,\n        UsageFlagE  = 0x04,\n        UsageFlagM  = 0x02,\n        UsageFlagX  = 0x01,\n    }\n\n    // the data we're importing from BSNES\n    private readonly byte[] usageMap;\n    \n    // our existing Diz project\n    private readonly ISnesData snesData;\n    \n    // if true, we'll only change things marked already as unreached.\n    // this is the safest way to go but, turning it off has a chance of correcting desync'd manual assembly,\n    // and that's a good thing.\n    private readonly bool onlyMarkIfUnreached;\n\n    public BsnesUsageMapImporter(byte[] usageMap, ISnesData snesData, bool onlyMarkIfUnreached = false)\n    {\n        this.usageMap = usageMap;\n        this.snesData = snesData;\n        this.onlyMarkIfUnreached = onlyMarkIfUnreached;\n    }\n\n    public int Run()\n    {\n        var modified = 0;\n        for (var snesOffset = 0; snesOffset <= 0xFFFFFF; snesOffset++)\n        {\n            if (ProcessUsageMapAddress(snesOffset))\n                modified++;\n        }\n\n        return modified;\n    }\n\n    // return true if we modified this address\n    private bool ProcessUsageMapAddress(int snesOffset)\n    {\n        var pc = snesData.ConvertSnesToPc(snesOffset);\n\n        // branch predictor may optimize this\n        if (pc == -1 || pc >= snesData.GetRomSize())\n            return false;\n\n        var bsnesByteFlags = (BsnesPlusUsage)usageMap[snesOffset];\n\n        // no information available\n        if (bsnesByteFlags == 0)\n            return false;\n\n        var existingDizByteType = snesData.GetFlag(pc);\n        \n        if (onlyMarkIfUnreached && existingDizByteType != FlagType.Unreached)\n            return false;\n\n        var changed = false;\n        \n        // theoretically, these can overlap too (Read + Exec an opcode)\n        // we prioritize marking bytes as code vs data\n        if (bsnesByteFlags.HasFlag(BsnesPlusUsage.UsageExec))\n        {\n            if (bsnesByteFlags.HasFlag(BsnesPlusUsage.UsageOpcode))\n            {\n                // note: BSNES gets MX flags right MOST of the time. it can happen where it reports something that's wrong\n                // and we blow away working data with non-good data. it happens.\n                \n                if (existingDizByteType != FlagType.Opcode)\n                    changed = true;\n\n                snesData.MarkAsOpcodeAndOperandsStartingAt(\n                    offset: pc,\n                    xFlag: bsnesByteFlags.HasFlag(BsnesPlusUsage.UsageFlagX),\n                    mFlag: bsnesByteFlags.HasFlag(BsnesPlusUsage.UsageFlagM)\n                );\n            }\n            else\n            {\n                // it's an operand.\n                // theoretically, it should be covered by the previous opcode calling MarkAsOpcodeAndOperandsStartingAt().\n                // we'll set it again just in case.\n                if (existingDizByteType != FlagType.Operand)\n                    changed = true;\n                \n                // note: MX flags not given to use by BSNES for this,\n                // we only get them for the opcode that came before this.\n            }\n        }\n        else if (bsnesByteFlags.HasFlag(BsnesPlusUsage.UsageRead))\n        {\n            if (existingDizByteType == FlagType.Unreached)\n            {\n                snesData.SetFlag(pc, FlagType.Data8Bit);\n                changed = true;\n            }\n        }\n        \n        return changed;\n    }\n}"
  },
  {
    "path": "Diz.Test/Diz.Test.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <UseWindowsForms>false</UseWindowsForms>\n    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>\n    <RootNamespace>Diz.Test</RootNamespace>\n    <AssemblyName>Diz.Test</AssemblyName>\n    <TargetFramework>net9.0</TargetFramework>\n    <LangVersion>latest</LangVersion>\n    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n    <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"BenchmarkDotNet\">\n      <Version>0.13.12</Version>\n    </PackageReference>\n    <PackageReference Include=\"ExtendedXmlSerializer\">\n      <Version>3.9.6</Version>\n    </PackageReference>\n    <PackageReference Include=\"FluentAssertions\">\n      <Version>6.12.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"FluentAssertions.Analyzers\">\n      <Version>0.29.1</Version>\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"FluentAssertions.ArgumentMatchers.Moq\">\n      <Version>3.1.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"JetBrains.Annotations\">\n      <Version>2023.3.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"LightInject\">\n      <Version>6.6.4</Version>\n    </PackageReference>\n    <PackageReference Include=\"Microsoft.CodeAnalysis.Analyzers\">\n      <Version>3.3.4</Version>\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\">\n      <Version>17.8.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"Moq\">\n      <Version>4.20.70</Version>\n    </PackageReference>\n    <PackageReference Include=\"xunit\">\n      <Version>2.6.6</Version>\n    </PackageReference>\n    <PackageReference Include=\"xunit.abstractions\">\n      <Version>2.0.3</Version>\n    </PackageReference>\n    <PackageReference Include=\"xunit.runner.console\">\n      <Version>2.6.6</Version>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n    <PackageReference Include=\"xunit.analyzers\">\n      <Version>1.10.0</Version>\n    </PackageReference>\n    <PackageReference Include=\"xunit.assert\">\n      <Version>2.6.6</Version>\n    </PackageReference>\n    <PackageReference Include=\"xunit.runner.visualstudio\">\n      <Version>2.5.6</Version>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n    <PackageReference Include=\"System.Resources.Extensions\" Version=\"8.0.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Diz.Controllers\\Diz.Controllers\\Diz.Controllers.csproj\" />\n    <ProjectReference Include=\"..\\Diz.Core\\Diz.Core.csproj\" />\n    <ProjectReference Include=\"..\\Diz.LogWriter\\Diz.LogWriter.csproj\" />\n    <ProjectReference Include=\"..\\Diz.Cpu.65816\\Diz.Cpu.65816.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Remove=\"Resources\\asartestrun.asm\" />\n    <EmbeddedResource Include=\"Resources\\asartestrun.asm\" />\n    <None Remove=\"Resources\\serialize-dictionary-test.xml\" />\n    <EmbeddedResource Include=\"Resources\\serialize-dictionary-test.xml\" />\n    <None Remove=\"Resources\\emptyrom.asm\" />\n    <EmbeddedResource Include=\"Resources\\emptyrom.asm\" />\n    <None Remove=\"Resources\\test42.asm\" />\n    <EmbeddedResource Include=\"Resources\\samplerom-a-few-lines.asm\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Folder Include=\"TestData\" />\n    <Folder Include=\"Tests\\AsarTests\" />\n    <Folder Include=\"Tests\\Controllers\" />\n    <Folder Include=\"Utils\\Vendor\" />\n  </ItemGroup>\n\n</Project>"
  },
  {
    "path": "Diz.Test/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.Test.Utils;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizTestCompositionRoot))]"
  },
  {
    "path": "Diz.Test/Resources/asartestrun.asm",
    "content": "﻿                       hirom                                ;      |        |      ;  \n                                                            ;      |        |      ;  \n                                                            ;      |        |      ;  \n                       ORG $C00000                          ;      |        |      ;  \n                                                            ;      |        |      ;  \n                       STA.W SNES_VMADDL                    ;C00000|8D1621  |002116;  \n                                                            ;      |        |      ;  \n                       SNES_VMADDL = $002116                ;      |        |      ;  \n"
  },
  {
    "path": "Diz.Test/Resources/emptyrom.asm",
    "content": "﻿                                                            ;      |        |      ;\n                       lorom                                ;      |        |      ;\n                                                            ;      |        |      ;\n                                                            ;      |        |      ;\n                                                            ;      |        |      ;\n; contains any auto-generated defines from Diz.\n; auto-generated file DON'T edit\n"
  },
  {
    "path": "Diz.Test/Resources/samplerom-a-few-lines.asm",
    "content": "﻿                       lorom                                ;      |        |      ;  \n                       ORG $808000                          ;      |        |      ;  \n          CODE_808000: LDA.W Test_Data,X                    ;808000|BD5B80  |80805B;  \n                       STA.W $0100,X                        ;808003|9D0001  |800100;  \n               Test22: DEX                                  ;808006|CA      |      ;  \n                       BPL CODE_808000                      ;808007|10F7    |808000;  \n                       Test_Data = $80805B                  ;      |        |      ;  \n"
  },
  {
    "path": "Diz.Test/Resources/serialize-dictionary-test.xml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<SerializerDictionaryTest-TestRoot xmlns:sys=\"https://extendedxmlserializer.github.io/system\" xmlns:exs=\"https://extendedxmlserializer.github.io/v2\" xmlns:ns1=\"clr-namespace:Diz.Core.model;assembly=Diz.Core\">\n<Odw>\n<sys:Item Key=\"1\">\n<Value Text=\"Z test1\" />\n</sys:Item>\n<sys:Item Key=\"2\">\n<Value Text=\"Z test3\" />\n</sys:Item>\n</Odw>\n<Odw2>\n<sys:Item Key=\"100\">\n<Value Name=\"location1\" Comment=\"c1\" />\n</sys:Item>\n<sys:Item Key=\"200\">\n<Value Name=\"location2\" Comment=\"c2\" />\n</sys:Item>\n</Odw2>\n</SerializerDictionaryTest-TestRoot>"
  },
  {
    "path": "Diz.Test/Tests/LogCreatorTests/AssemblyStrings.cs",
    "content": "﻿using System.Collections.Generic;\nusing Diz.LogWriter;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Diz.Test.Tests.LogCreatorTests;\n\npublic class AssemblyStrings\n{\n    public static IEnumerable<object[]> TestHexData => new List<object[]>\n    {\n        new object[] { \"test text\", \"db \\\"test text\\\"\" },\n        new object[] { \"test text\\0\", \"db \\\"test text\\\", $00\" },\n        new object[] { \"\\0\\0 test text\\0123\\0hi\", \"db $00, $00, \\\" test text\\\", $00, \\\"123\\\", $00, \\\"hi\\\"\" },\n        \n        new object[]\n        {\n            \"\\0test1\\ntest\\0\\r\\n\\0\", \n            @\"db $00, \"\"test1\"\", $0A, \"\"test\"\", $00, $0D, $0A, $00\"\n        },\n    };\n    \n    [Theory]\n    [MemberData(nameof(TestHexData))]\n    public static void TestHexParse(string inputStr, string outputLine)\n    {\n        LogCreatorExtensions.CreateAssemblyFormattedTextLine(inputStr).Should().Be(outputLine);\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/LogCreatorTests/LogCreator.cs",
    "content": "﻿using System.Linq;\nusing Diz.Core;\nusing Diz.Core.export;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\nusing Diz.Cpu._65816;\nusing Diz.Test.Utils;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test.Tests.LogCreatorTests;\n\npublic class LogCreatorTests : ContainerFixture\n{\n    private const string ExpectedRaw =\n        //          label:       instructions                         ;PC    |rawbytes|ia\n        \"                        lorom                                ;      |        |      ;  \\r\\n\" +\n        \"                                                             ;      |        |      ;  \\r\\n\" +\n        \"                                                             ;      |        |      ;  \\r\\n\" +\n        \"                        ORG $808000                          ;      |        |      ;  \\r\\n\" +\n        \"                                                             ;      |        |      ;  \\r\\n\" +\n        \"           CODE_808000: LDA.W Test_Data,X                    ;808000|BD5B80  |80805B;  \\r\\n\" +\n        \"                        STA.W $0100,X                        ;808003|9D0001  |800100;  \\r\\n\" +\n        \"           Test22:      DEX                                  ;808006|CA      |      ;  \\r\\n\" +\n        \"                        BPL CODE_808000                      ;808007|10F7    |808000;  \\r\\n\" +\n        \"                                                             ;      |        |      ;  \\r\\n\" +\n        \"                        Test_Data = $80805B                  ;      |        |      ;  \\r\\n\";\n\n    [Inject] private readonly IDataFactory dataFactory = null!;\n    \n    [Fact(Skip = \"need to reset the .asm file\")]\n    public void TestAFewLines()\n    {\n        var data = CreateSampleData();\n        LogWriterHelper.AssertAssemblyOutputEquals(ExpectedRaw, LogWriterHelper.ExportAssembly(data, creator =>\n        {\n            creator.Settings = new LogWriterSettings\n            {\n                OutputExtraWhitespace = false    \n            };\n        }), debugWriter);\n    }\n\n    [Fact]\n    public void TestLabelCount()\n    {\n        var data = CreateSampleData();\n            \n        // should give us \"Test22\" and \"Test_Data\"\n        Assert.Equal(2, data.Labels.Labels.Count());\n    }\n    \n    [Fact(Skip = \"need to reset the .asm file\")]\n    public void TestOneLine()\n    {\n        var exportAssembly = LogWriterHelper.ExportAssembly(dataFactory.Create());\n        LogWriterHelper.AssertAssemblyOutputEquals(ExpectedRaw, exportAssembly);\n    }\n        \n    [Theory]\n    [EmbeddedResourceData(\"Diz.Test/Resources/emptyrom.asm\")]\n    public void TestEmptyRom(string expectedAsm)\n    {\n        var emptyData = dataFactory.Create();\n        emptyData.Apis.AddIfDoesntExist(new SnesApi(emptyData));\n        var result = LogWriterHelper.ExportAssembly(emptyData);\n        LogWriterHelper.AssertAssemblyOutputEquals(expectedAsm, result, debugWriter);\n    }\n\n    private readonly ITestOutputHelper debugWriter;\n    public LogCreatorTests(ITestOutputHelper debugWriter)\n    {\n        this.debugWriter = debugWriter;\n    }\n    \n    private Data CreateSampleData()\n    {\n        var data = dataFactory.Create();\n        \n        data.RomMapMode = RomMapMode.LoRom;\n        data.RomSpeed = RomSpeed.FastRom;\n        data.RomBytes = new RomBytes\n        {\n            // --------------------------\n            // highlighting a particular section here\n            // we will use this for unit tests as well.\n\n            // CODE_808000: LDA.W Test_Data,X\n            new()\n            {\n                Rom = 0xBD, TypeFlag = FlagType.Opcode, MFlag = true, Point = InOutPoint.InPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0x5B, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 }, // Test_Data\n            new() { Rom = 0x80, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 }, // Test_Data\n\n            // STA.W $0100,X\n            new() { Rom = 0x9D, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x00, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n            new() { Rom = 0x01, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n\n            // DEX\n            new() { Rom = 0xCA, TypeFlag = FlagType.Opcode, MFlag = true, DataBank = 0x80, DirectPage = 0x2100 },\n\n            // BPL CODE_808000\n            new()\n            {\n                Rom = 0x10, TypeFlag = FlagType.Opcode, MFlag = true, Point = InOutPoint.OutPoint, DataBank = 0x80,\n                DirectPage = 0x2100\n            },\n            new() { Rom = 0xF7, TypeFlag = FlagType.Operand, DataBank = 0x80, DirectPage = 0x2100 },\n        };\n\n        data.Labels.AddLabel(0x808000 + 0x06, new Label { Name = \"Test22\" });\n        data.Labels.AddLabel(0x808000 + 0x5B, new Label { Name = \"Test_Data\", Comment = \"Pretty cool huh?\" });\n        return data;\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/PerformanceTests/TraceLogPerformanceTests.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Loggers;\nusing Diz.Core.model.snes;\nusing Diz.Cpu._65816;\nusing Diz.Import.bsnes.tracelog;\nusing Diz.Test.Utils;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test\n{\n    public sealed class XOutLogger : ILogger\n    {\n        private readonly ITestOutputHelper _helper;\n\n        public XOutLogger(ITestOutputHelper helper)\n        {\n            _helper = helper;\n        }\n\n        public void Write(LogKind logKind, string text)\n        {\n            _helper.WriteLine(text); // not quite correct but works.\n        }\n\n        public void WriteLine()\n        {\n            _helper.WriteLine(\"\\n\");\n        }\n\n        public void WriteLine(LogKind logKind, string text)\n        {\n            _helper.WriteLine(text);\n        }\n\n        public void Flush()\n        {\n        }\n\n        public string Id { get; } = \"xunitLogger\";\n        public int Priority { get; }\n    }\n\n    public class TraceLogPerformanceTests\n    {\n        private readonly ITestOutputHelper output;\n\n        public TraceLogPerformanceTests(ITestOutputHelper output)\n        {\n            this.output = output;\n        }\n\n        private void RunPrintTiming(Action item)\n        {\n            var s = Stopwatch.StartNew();\n            s.Start();\n                \n            item.Invoke();\n                \n            s.Stop();\n            output.WriteLine($\"runtime: {s.ElapsedMilliseconds:N0}ms\");\n        }\n\n        // public class ImportTraceLogStreamTestHarness\n        // {\n        //     const string datafile = \"..\\\\..\\\\testdata\\\\ct-binary-tracelog7.758s-60fps-locked.bin\";\n        //     readonly Data Data = new EmptyRom();\n        //     private readonly BsnesTraceLogDebugBenchmarkFileCapture capturing;\n        //     private readonly Stopwatch stopWatch = new();\n        //     private readonly ITestOutputHelper output;\n        //\n        //     public ImportTraceLogStreamTestHarness(ITestOutputHelper output)\n        //     {\n        //         this.output = output;\n        //\n        //         var cwd = Directory.GetCurrentDirectory();\n        //         var fullPath = Path.Combine(cwd, datafile);\n        //\n        //         capturing = new BsnesTraceLogDebugBenchmarkFileCapture(fullPath, 1)\n        //         {\n        //             OnStart = () => { stopWatch.Reset(); stopWatch.Start(); },\n        //             OnStop = () =>\n        //             {\n        //                 stopWatch.Stop();\n        //                 this.output.WriteLine($\"runtime: {stopWatch.ElapsedMilliseconds:N0}ms\");\n        //             }\n        //         };\n        //     }\n        //\n        //     [Benchmark]\n        //     public void Run()\n        //     {\n        //         capturing.Run(Data.GetSnesApi());\n        //     }\n        // }\n\n        // [Fact(Skip=\"skipping b/c external dependency\")]\n        // public void TestTraceLogPerformance()\n        // {\n        //     var test = new ImportTraceLogStreamTestHarness(output);\n        //     test.Run();\n        //     // XunitBenchmark.Run<ImportTraceLogStream>(output);\n        // }\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/ProjectSessionTest.cs",
    "content": "﻿using Diz.Core.model;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Diz.Test.Tests;\n\npublic class ProjectSessionTest\n{\n    [Fact]\n    void TestProjectSessionUnsaved()\n    {\n        var sampleProject = new Project();\n\n        sampleProject.Session = new ProjectSession(sampleProject, \"test\");\n\n        sampleProject.Session.UnsavedChanges.Should().BeFalse();\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/RomInterfaceTests/AccessTests.cs",
    "content": "﻿// everything here was migrated to AccessTests.cs\n\nusing System.Collections.Generic;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\nusing Diz.Cpu._65816;\nusing Diz.Test.Utils;\nusing Xunit;\n\nnamespace Diz.Test.Tests.RomInterfaceTests\n{\n    public static class CpuTests\n    {\n        private static Data GetSampleData()\n        {\n            var data = new Data\n            {\n                RomMapMode = RomMapMode.HiRom,\n                RomSpeed = RomSpeed.FastRom,\n                RomBytes = new RomBytes\n                {\n                    // starts at PC=0, which is SNES=0xC00000\n                    // STA.W SNES_VMADDL\n                    // OR (equivalent)\n                    // STA.W $2116\n                    new()\n                    {\n                        Rom = 0x8D, TypeFlag = FlagType.Opcode, MFlag = true, XFlag = true, DataBank = 0x00,\n                        DirectPage = 0,\n                    },\n                    new() { Rom = 0x16, TypeFlag = FlagType.Operand },\n                    new() { Rom = 0x21, TypeFlag = FlagType.Operand },\n                },\n                Comments = new SortedDictionary<int, string>\n                {\n                    { 0xC00001, \"unused\" },\n                }\n            };\n\n            foreach (var (k, v) in new Dictionary<int, Label>\n            {\n                {0x002116, new Label { Name = \"SNES_VMADDL\", Comment = \"SNES hardware register example.\" }}\n            })\n            {\n                data.Labels.AddLabel(k, v);\n            }\n            \n            data.Apis.AddIfDoesntExist(new SnesApi(data));\n            \n            return data;\n        }\n\n        public static IReadOnlyList<byte> AssemblyRom => AsarRunner.AssembleToRom(@\"\n            hirom\n\n            SNES_VMADDL = $002116\n            ; SNES_VMADDL = $7E2116\n\n            ORG $C00000\n\n            STA.W SNES_VMADDL\"\n        );\n\n        [Fact]\n        public static void SanityTest()\n        {\n            var data = GetSampleData();\n            Assert.Equal(0x8D, data.GetRomByte(0) ?? 0);\n            Assert.Equal(0x16, data.GetRomByte(1) ?? 0);\n            Assert.Equal(0x21, data.GetRomByte(2) ?? 0);\n            Assert.Equal(3, data.GetRomSize());\n\n            Assert.Equal(\"SNES_VMADDL\", data.Labels.GetLabel(0x2116).Name);\n            Assert.Null(data.Labels.GetLabel(0x2119)); // bogus address\n            // Assert.Equal(\"SNES_VMADDL\", data.GetLabelName(0x7E2116)); // later, we need this to ALSO work\n        }\n\n        [Fact]\n        public static void IA1()\n        {\n            var data = GetSampleData();\n            Assert.Equal(0x002116, data.GetSnesApi().GetIntermediateAddressOrPointer(0));\n        }\n\n        [Fact]\n        public static void IA2()\n        {\n            var data = GetSampleData();\n            data.RomBytes[0].DataBank = 0x7E;\n            Assert.Equal(0x7E2116, data.GetSnesApi().GetIntermediateAddressOrPointer(0));\n        }\n\n        [Fact(Skip = \"Relies on external tool that isn't yet setup\")]\n        public static void RunTestRom()\n        {\n            // C# ROM -> Assembly Text \n            var exportAssembly = LogWriterHelper.ExportAssembly(GetSampleData()).AssemblyOutputStr;\n            \n            // Assembly Text -> Asar -> SFC file\n            var bytes = AsarRunner.AssembleToRom(exportAssembly);\n            \n            Assert.Equal(3, bytes.Count);\n            \n            Assert.Equal(0x8D, bytes[0]);\n            Assert.Equal(0x16, bytes[1]);\n            Assert.Equal(0x21, bytes[2]);\n        } \n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/RomInterfaceTests/CartTitleTests.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Xml;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.util;\nusing Diz.Cpu._65816;\nusing Diz.Test.Utils;\nusing Diz.Test.Utils.SuperFamiCheckUtil;\nusing ExtendedXmlSerializer;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Diz.Test.Tests.RomInterfaceTests;\n\npublic static class CartNameData\n{\n    // note: you need to put this on your local system for it to work.\n    // TODO: gotta figure out how to make this portable without running into weirdness.\n    public const string ExampleHiRomFile = @\"D:\\roms\\SNES\\ct (U) [!].smc\";\n    public const string ExampleLoRomFile = @\"D:\\roms\\SNES\\Super Mario World (U) [!].smc\";\n}\n    \npublic class CartNameTests : ContainerFixture\n{\n    [Inject] private readonly IXmlSerializerFactory serializerFactory = null!;\n    [Inject] private readonly ISnesSampleProjectFactory sampleData = null!;\n\n    // Bytes for a Cart Name from a SNES header\n    // \"Marvelous - Mouhitotsu no Takara-jima (Japan).sfc\"\n    // StartOffset(h): 00007FC0, EndOffset(h): 00007FD4, Length(h): 00000015\n    // 21 bytes\n    private static byte[] RawRomBytes => new byte[]\n    {\n        0xCF, 0xB0, 0xB3, 0xDE, 0xAA, 0xD7, 0xBD, 0x20, 0x20, 0x20,\n        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\n        0x20\n    };\n\n    private static string ExpectedTitleStr => \"ﾏｰｳﾞｪﾗｽ\".PadRight(RomUtil.LengthOfTitleName);\n\n    // bugfix credit: @LuigiBlood and #diztinguish on Discord\n    [Fact]\n    public void TestTitleFilenameConversions()\n    {\n        RawRomBytes.Length.Should().Be(RomUtil.LengthOfTitleName);\n\n        // convert to UTF8 bytes\n        var shiftJisEncoding = ByteUtil.ShiftJisEncoding;\n        var utfBytes = Encoding.Convert(shiftJisEncoding, Encoding.UTF8, RawRomBytes);\n        utfBytes.Length.Should().Be(35);\n\n        // convert to UTF8 string\n        var utfStrParsed = Encoding.UTF8.GetString(utfBytes);\n        utfStrParsed.Should().Be(ExpectedTitleStr);\n\n        // convert back to UTF8 bytes\n        var utfBytesFromStr = Encoding.UTF8.GetBytes(utfStrParsed);\n        utfBytesFromStr.Should().BeEquivalentTo(utfBytes);\n        utfBytesFromStr.Length.Should().Be(35);\n\n        // convert back to Shift-JIS\n        var shiftJisConvertBytes = Encoding.Convert(Encoding.UTF8, shiftJisEncoding, utfBytesFromStr);\n        shiftJisConvertBytes.Should().BeEquivalentTo(RawRomBytes);\n    }\n\n    [Fact]\n    public void TestTitleRead()\n    {\n        var fakeRom = Enumerable\n            .Range(0, 0x7FC0)\n            .Select(_ => (byte) 0x00)\n            .Concat(RawRomBytes)\n            .ToArray();\n\n        RomUtil\n            .GetCartridgeTitleFromRom(\n                fakeRom,\n                RomUtil.GetRomSettingOffset(RomMapMode.LoRom)\n            ).Should().Be(ExpectedTitleStr);\n    }\n\n    private class TestRoot\n    {\n        public string CartTitle { get; set; }\n    }\n\n    [Fact]\n    public void TestXmlCycle3()\n    {\n        var serializer = serializerFactory.GetSerializer(null).Create();\n\n        var xmlStr = serializer.Serialize(\n            new XmlWriterSettings(),\n            new TestRoot {CartTitle = ExpectedTitleStr}\n        );\n        var restoredRoot = serializer.Deserialize<TestRoot>(xmlStr);\n\n        xmlStr.Should().Contain($\"CartTitle=\\\"{ExpectedTitleStr}\\\"\");\n\n        restoredRoot.CartTitle.Should().Be(ExpectedTitleStr);\n    }\n\n    [Fact]\n    public void CartNameInHeader()\n    {\n        // use the sample data to fake a project\n        var srcProject = sampleData.Create() as Project;\n        var expectedTitle = SnesSampleRomDataFactory.GetSampleUtf8CartridgeTitle();\n        TestRomCartTitle(srcProject, expectedTitle);\n    }\n        \n    internal static void TestRomCartTitle(Project project, string expectedTitle)\n    {\n        ByteUtil.ConvertUtf8ToShiftJisEncodedBytes(project.InternalRomGameName)\n            .Should().HaveCount(RomUtil.LengthOfTitleName,\n                \"SNES cart name in header must be exactly this many bytes\");\n\n        project.Data.GetSnesApi().CartridgeTitleName\n            .Should().Be(project.InternalRomGameName, \"it should be identical to the cached name\");\n\n        var trimmedTitle = project.InternalRomGameName.TrimEnd(' ');\n        trimmedTitle.Should().Be(expectedTitle, \"SNES headers are padded with spaces at the end to a fixed size\");\n    }\n        \n    [Fact]\n    public void TestCartChecksumInHeader()\n    {\n        // use the sample data to fake a project\n        var srcProject = sampleData.Create() as Project;\n        srcProject.Data.GetSnesApi().RomChecksum.Should().Be(srcProject.Data.GetSnesApi().ComputeChecksum(),\n            \"checksum bytes in the ROM should match the computed checksum\");\n    }\n\n    [FactOnlyIfFilePresent(new[]{SuperFamiCheckTool.Exe, CartNameData.ExampleHiRomFile})]\n    public void TestFamicheckTool()\n    {\n        var result = SuperFamiCheckTool.Run(CartNameData.ExampleHiRomFile);\n        result.Complement.Should().Be(0x8773);\n        result.Checksum.Should().Be(0x788c);\n\n        // it's stored in the ROM file like this:\n        // 73 87 8C 78\n    }\n}\n\npublic class TestChecksums : ContainerFixture\n{\n    [Inject] private readonly IProjectImporter projectImporter = null!;\n\n    [FactOnlyIfFilePresent(new[]{SuperFamiCheckTool.Exe, CartNameData.ExampleHiRomFile})]\n    public void TestInternalChecksumVsExternal()\n    {\n        var result = SuperFamiCheckTool.Run(CartNameData.ExampleHiRomFile);\n        result.Complement.Should().Be(0x8773);\n        result.Checksum.Should().Be(0x788c);\n        (result.Complement + result.Checksum).Should().Be(0xFFFF);\n            \n        const uint expected4ByteChecksums = 0x788C8773;\n        result.AllCheckBytes.Should().Be(expected4ByteChecksums);\n\n        var project = projectImporter.CreateProjectFromDefaultSettings(CartNameData.ExampleHiRomFile);\n        project.Should().NotBeNull(\"project should have loaded successfully\");\n        project.Data.GetRomByte(0xFFDC).Should().Be(0x73); // complement 1\n        project.Data.GetRomByte(0xFFDD).Should().Be(0x87); // complement 2\n        project.Data.GetRomByte(0xFFDE).Should().Be(0x8C); // checksum 1\n        project.Data.GetRomByte(0xFFDF).Should().Be(0x78); // checksum 2\n\n        var complement = project.Data.GetRomWord(0xFFDC);\n        complement.Should().Be(0x8773); // complement 16bit\n\n        var checksum = project.Data.GetRomWord(0xFFDE);\n        checksum.Should().Be(0x788c); // checksum 16bit\n            \n        (complement + checksum).Should().Be(0xFFFF);\n\n        var snesApi = project.Data.GetSnesApi();\n        Debug.Assert(snesApi != null);\n            \n        project.Data.GetRomDoubleWord(0xFFDC).Should().Be((int) expected4ByteChecksums); // complement 16bit\n        snesApi.RomCheckSumsFromRomBytes.Should().Be(expected4ByteChecksums);\n        project.InternalCheckSum.Should().Be(expected4ByteChecksums);\n\n        result.Complement.Should().Be((uint) complement);\n        result.Checksum.Should().Be((uint) checksum);\n\n        snesApi.ComputeChecksum().Should().Be((ushort)checksum);\n        snesApi.ComputeIsChecksumValid().Should().Be(true);\n\n        var firstByte = project.Data.RomBytes[0x00].Rom;\n        firstByte.Should().NotBe(0);\n        project.Data.RomBytes[0x00].Rom = 0;\n        snesApi.ComputeIsChecksumValid().Should().Be(false);\n        snesApi.FixChecksum();\n        snesApi.ComputeIsChecksumValid().Should().Be(true);\n            \n        project.Data.RomBytes[0x00].Rom = firstByte;\n        snesApi.ComputeIsChecksumValid().Should().Be(false);\n        snesApi.FixChecksum();\n        snesApi.ComputeIsChecksumValid().Should().Be(true);\n            \n        // SNES docs dictate:\n        // 15. Complement Check (0xFFDC, 0xFFDD)\n        // 16. Check Sum (0xFFDE, 0xFFDF)\n\n        // in the actual ROM file, it appears like this (remember: little endian for SNES)\n        // complement   checksum\n        // 73 87        8C 78\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/RomInterfaceTests/TestServiceInterfaces.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Diz.Controllers.interfaces;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing LightInject;\nusing Moq;\nusing Xunit;\n\nnamespace Diz.Test.Tests.RomInterfaceTests;\n\n/// <summary>\n/// Look through all registered interfaces in the service container, try and instantiate\n/// each one and make sure nothing breaks at runtime. Failed tests typically indicate you need\n/// to register class dependencies in the CompositionRoot classes.\n/// </summary>\npublic class TestServiceInterfaces : ContainerFixture\n{\n    public static IEnumerable<object[]> Interfaces =>\n        CreateAndRegisterServiceContainer().AvailableServices\n            .Select(x => x.ServiceType)\n            .Select(type => new[] { type });\n\n    protected override void Configure(IServiceRegistry serviceRegistry)\n    {\n        MockGuiInterfaces(serviceRegistry);\n    }\n\n    private static void MockGuiInterfaces(IServiceRegistry serviceRegistry)\n    {\n        serviceRegistry.Register(_ => new Mock<ICommonGui>().Object);\n        serviceRegistry.Register(_ => new Mock<IImportRomDialogView>().Object);\n    }\n\n    [Theory(Skip = \"Useful more for debugging registrations, less so as a comprehensive unit test\"), MemberData(nameof(Interfaces))]\n    public void CreateInstance(Type interfaceToTest)\n    {\n        ServiceFactory.GetInstance(interfaceToTest).Should().NotBeNull();\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/RomModeDetectionTests.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model.project;\nusing Diz.Core.util;\nusing Diz.Cpu._65816.import;\nusing Diz.Test.Tests.RomInterfaceTests;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing LightInject;\nusing Xunit;\n\nnamespace Diz.Test.Tests;\n\npublic class RomModeDetectionTests : ContainerFixture\n{\n    [Inject] private readonly IReadFromFileBytes fileReader = null!;\n    [Inject] private readonly ISampleRomTestData sampleDataFixture = null!;\n\n    protected override void Configure(IServiceRegistry serviceRegistry)\n    {\n        base.Configure(serviceRegistry);\n        serviceRegistry.Register<ISampleRomTestData, SampleRomTestDataFixture>();\n    }\n\n    [FactOnlyIfFilePresent(new[]{CartNameData.ExampleLoRomFile})]\n    public void TestRomDetectionLoRom()\n    {\n        var detectRomMapMode = RomUtil.DetectRomMapMode(fileReader.ReadRomFileBytes(CartNameData.ExampleLoRomFile), out var detectedValidRomMapType);\n        detectedValidRomMapType.Should().Be(true);\n        detectRomMapMode.Should().Be(RomMapMode.LoRom);\n    }\n    \n    [FactOnlyIfFilePresent(new[]{CartNameData.ExampleHiRomFile})]\n    public void TestRomDetectionHiRom()\n    {\n        var detectRomMapMode = RomUtil.DetectRomMapMode(\n            fileReader.ReadRomFileBytes(CartNameData.ExampleHiRomFile), \n            out var detectedValidRomMapType);\n        \n        detectedValidRomMapType.Should().Be(true);\n        detectRomMapMode.Should().Be(RomMapMode.HiRom);\n    }\n    \n    [FactOnlyIfFilePresent(new[]{CartNameData.ExampleHiRomFile})]\n    public void TestRomDetectionFastRom()\n    {\n        var analyzer = new SnesRomAnalyzer();\n        analyzer.Analyze(CartNameData.ExampleHiRomFile);\n\n        analyzer.Filename.Should().Be(CartNameData.ExampleHiRomFile);\n        analyzer.AnalysisResults!.RomMapMode.Should().Be(RomMapMode.HiRom);\n        analyzer.AnalysisResults.RomSpeed.Should().Be(RomSpeed.FastRom);\n        analyzer.RomBytes.Should().HaveCount(0x400000);\n        analyzer.RomSettingsOffset.Should().Be(RomUtil.HiromSettingOffset);\n    }\n    \n    [Fact]\n    public void TestRomDetectionGeneratedRom()\n    {\n        sampleDataFixture.Should().NotBeNull();\n        var detectRomMapMode = RomUtil.DetectRomMapMode(sampleDataFixture.SampleRomBytes, out var detectedValidRomMapType);\n        detectedValidRomMapType.Should().Be(true);\n        detectRomMapMode.Should().Be(RomMapMode.LoRom);\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/SerializationTests/CompressionTest.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing Diz.Core.serialization.xml_serializer;\nusing Xunit;\n\nnamespace Diz.Test\n{\n    public static class CompressionTest\n    {\n        private static IEnumerable<TOut> Repeat<TOut>(TOut toRepeat, int times) {\n            for (var i = 0; i < times; ++i) {\n                yield return toRepeat;\n            }\n        }\n\n        private static (IEnumerable<string>, IEnumerable<string>) GenerateRepeat(string toRepeat, int times)\n        {\n            var repeated = Repeat($\"{toRepeat}\", times);\n            return (repeated.ToList(),\n                    times >= 8\n                        ? new List<string>(new[] {$\"r {times} {toRepeat}\"})\n                        : repeated.ToList()\n                );\n        }\n\n        public static TheoryData<IEnumerable<string>, IEnumerable<string>> ValidCompressionData\n        {\n            get\n            {\n                // TODO: probably a way simpler way to do this. works ok.\n                var p = ValidDataReal();\n                var xx = new TheoryData<IEnumerable<string>, IEnumerable<string>>();\n                p.ForEach(i => xx.Add(i.Item1, i.Item2));\n                return xx;\n            }\n        }\n\n        private static List<(IEnumerable<string>, IEnumerable<string>)> ValidDataReal() =>\n            new()\n            {\n                GenerateRepeat(\"TestItem\", 20),\n                GenerateRepeat(\"TestItem\", 30),\n                (\n                    Repeat(\"YO\", 20).Concat(new[] {\n                        \"different @ end\"\n                    }),\n                    new List<string>(new[] {\n                        \"r 20 YO\",\n                        \"different @ end\"\n                    })\n                ),\n                (\n                    new List<string>()\n                    {\n                        \"start\",\n                        \"start2\",\n                    }\n                        .Concat(\n                        Repeat(\"YO1\", 22).Concat(\n                            Repeat(\"YO2\", 20).Concat(\n                                new[] {\n                                \"different @ end\"\n                            }))),\n                    new List<string>(new[] {\n                        \"start\",\n                        \"start2\",\n                        \"r 22 YO1\",\n                        \"r 20 YO2\",\n                        \"different @ end\"\n                    })\n                )\n            };\n\n        [Theory]\n        [MemberData(nameof(ValidCompressionData))]\n        public static void TestCompressionsValid(IEnumerable<string> input, IEnumerable<string> expected)\n        {\n            var inputListCopy = new List<string>(input);\n            RepeaterCompression.Compress(ref inputListCopy);\n            Assert.Equal(expected, inputListCopy);\n            \n            RepeaterCompression.Decompress(ref inputListCopy);\n            Assert.Equal(inputListCopy, input);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/SerializationTests/LoadSavePerformanceTests.cs",
    "content": "﻿using System.Diagnostics;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\nusing Diz.Test.Utils;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test;\n\npublic class LoadSavePerformanceTests : ContainerFixture\n{\n    private readonly IProjectFileManager projectFileManager = null!;\n    private readonly IProjectXmlSerializer projectXmlSerializer = null!;\n    \n    private readonly ITestOutputHelper output;\n    public LoadSavePerformanceTests(ITestOutputHelper output)\n    {\n        this.output = output;\n    }\n    \n    private static Project OpenProject(string openFile, IProjectFileManager projectFileManager)\n    {\n        var projectOpenResult = projectFileManager.Open(openFile);\n\n        Assert.Empty(projectOpenResult.OpenResult.Warnings);\n        var project = projectOpenResult.Root.Project;\n        Assert.True(project.Data.RomBytes.Count >= 0x1000 * 64);\n            \n        return project;\n    }\n\n    [Fact(Skip = \"Performance Test\")]\n    private void OpenFilePerformanceTest()\n    {\n        var s = Stopwatch.StartNew();\n        s.Start();\n            \n        const string openFile = \"INSERT YOUR FILE HERE BEFORE RUNNING THIS TEST.dizraw\";\n        var project = OpenProject(openFile, projectFileManager);\n\n        s.Stop();\n\n        output.WriteLine($\"runtime: {s.ElapsedMilliseconds:N0}, #bytes={project.Data.RomBytes.Count}\");\n    }\n\n\n    [Fact(Skip = \"Performance Test\")]\n    private void SaveFilePerformanceTest()\n    {\n        const string openFile = \"INSERT YOUR FILE HERE BEFORE RUNNING THIS TEST.dizraw\";\n        var project = OpenProject(openFile, projectFileManager);\n            \n        var s = Stopwatch.StartNew();\n        s.Start();\n\n        var data = projectXmlSerializer.Save(project);\n\n        s.Stop();\n            \n        Assert.True(data.Length != 0);\n\n        output.WriteLine($\"runtime: {s.ElapsedMilliseconds:N0}\");\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/SerializationTests/MigrationTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing FluentAssertions;\nusing Moq;\nusing Xunit;\n\nnamespace Diz.Test\n{\n    public class MigrationTests\n    {\n        \n        [Theory]\n        [MemberData(nameof(Harnesses))]\n        public void TestMigrationFailsIfOutsideBounds(Harness harness)\n        {\n            var runner = new MigrationRunner(harness.MigrationObjs) {\n                StartingSaveVersion = harness.RunnerStart,\n                TargetSaveVersion = harness.RunnerTarget,\n            };\n            \n            harness.Run(() => runner.OnLoadingAfterAddLinkedRom(null));\n            harness.Verify();\n        }\n\n        [Fact] public static void TestNullElementFails() =>\n            new MigrationRunner { Migrations = {null} }.Invoking(r=>r.OnLoadingAfterAddLinkedRom(null))\n                .Should().Throw<DizMigrationException>().WithMessage(\"*all migrations must be non-null*\");\n        \n        [Fact] public static void TestInvalidStartAndTarget() =>\n            new MigrationRunner { StartingSaveVersion = 5, TargetSaveVersion = 4}.Invoking(r=>r.OnLoadingAfterAddLinkedRom(null))\n                .Should().Throw<DizMigrationException>().WithMessage(\"*starting migration version is greater than target version*\");\n\n        #region HarnessData\n        public static TheoryData<Harness> Harnesses => new()\n        {\n            new Harness\n            {\n                RunnerStart = 100,\n                RunnerTarget = 101,\n                Migrations =\n                {\n                    CreateMigrationMock(99,  false, \"outtaRangeLow\"),\n                    CreateMigrationMock(100,  true, \"inRange1\"),\n                    CreateMigrationMock(100,  true, \"inRange2\"),\n                    CreateMigrationMock(101,  false, \"outtaRangeHigh\"),\n                }\n            },\n            new Harness\n            {\n                ExpectedException = \"*migration out of sequence. version 102 not valid here. needed to upgrade from 100*\",\n                RunnerStart = 100,\n                RunnerTarget = 103,\n                Migrations =\n                {\n                    CreateMigrationMock(100,  true),\n                    // leave a gap, should throw error.\n                    CreateMigrationMock(102,  false),\n                }\n            },\n            new Harness\n            {\n                ExpectedException = \"*all migrations must >= other migrations in the sequence*\",\n                RunnerStart = 100,\n                RunnerTarget = 101,\n                Migrations =\n                {\n                    CreateMigrationMock(101,  false),\n                    CreateMigrationMock(100,  false),\n                }\n            },\n            new Harness {\n                ExpectedException = \"*migration failed. we were trying to*\",\n                RunnerStart = 100, RunnerTarget = 101,\n                Migrations = {\n                    CreateMigrationMock(99,  false),\n                    CreateMigrationMock(99,  false),\n                }\n            },\n            new Harness {\n                ExpectedException = \"*migration failed. we were trying to*\",\n                RunnerStart = 100, RunnerTarget = 150,\n                Migrations = {\n                    CreateMigrationMock(100,  true),\n                    CreateMigrationMock(101,  true),\n                }\n            },\n            new Harness {\n                RunnerStart = 100, RunnerTarget = 100,\n                Migrations = {\n                    CreateMigrationMock(99,  false),\n                    CreateMigrationMock(100,  false),\n                    CreateMigrationMock(101,  false),\n                }\n            }\n            \n            // \n        };\n        #endregion\n\n        #region Backend\n\n        public class MigrationMock\n        {\n            public Mock<IMigration> Mock;\n            public Action<Mock<IMigration>> Verify;\n        }\n\n        private static MigrationMock CreateMigrationMock(int saveVersion, bool shouldHaveRun=false, string extraName = \"\")\n        {\n            var mock = new Mock<IMigration>(MockBehavior.Strict);\n\n            mock.SetupGet(x => x.AppliesToSaveVersion)\n                .Returns(saveVersion);\n\n            mock.Setup(x => x\n                .OnLoadingAfterAddLinkedRom(It.IsAny<IAddRomDataCommand>()));\n\n            mock.Name += $\"--{extraName}-- (v{mock.Object.AppliesToSaveVersion})\";\n            \n            return new MigrationMock\n            {\n                Mock = mock,\n                Verify = m => {\n                    m.Verify(\n                        migration => migration.OnLoadingAfterAddLinkedRom(null), \n                        Times.Exactly(shouldHaveRun ? 1 : 0));\n                }\n            };\n        }\n\n        public class Harness\n        {\n            public int RunnerStart;\n            public int RunnerTarget;\n            public string ExpectedException = null;\n            public readonly List<MigrationMock> Migrations = new();\n\n            public List<IMigration> MigrationObjs => Migrations.Select(mock => mock.Mock.Object).ToList();\n            \n            public void Verify()\n            {\n                foreach (var migration in Migrations)\n                {\n                    migration.Verify(migration.Mock);\n                }\n            }\n\n            public void Run(Action action)\n            {\n                if (ExpectedException == null)\n                {\n                    action();\n                }\n                else\n                {\n                    action.Should().Throw<DizMigrationException>()\n                        .WithMessage(ExpectedException);\n                }\n            }\n        }\n        \n        #endregion\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/SerializationTests/ProjectLoadSaveTest.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Diz.Core.model;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Cpu._65816;\nusing Diz.Test.Tests.RomInterfaceTests;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Diz.Test.Tests.SerializationTests;\n\npublic class LoadSaveTest : ContainerFixture\n{\n    [Inject] private readonly IProjectXmlSerializer serializer = null!;\n    [Inject] private readonly ISnesSampleProjectFactory sampleProjectCreator = null!;\n\n    [Fact]\n    public void FullSerializeAndDeserialize()\n    {\n        var srcProject = sampleProjectCreator.Create() as Project ?? throw new Exception(\"can't create sample project data\");\n            \n        var expectedTitle = SnesSampleRomDataFactory.GetSampleUtf8CartridgeTitle();\n\n        srcProject.Data.Comments.Count.Should().BePositive();\n        srcProject.Data.Labels.Labels.Count().Should().BePositive();\n\n        // extract the bytes that would normally be in the SMC file (they only exist in code for this sample data)\n        var romFileBytes = srcProject.Data.GetFileBytes().ToList();\n\n        // save it to create an output byte stream, we'd normally write this to the disk\n        \n        var outputBytes = serializer.Save(srcProject);\n\n        // now do the reverse and load our output back as the input\n        var projectOpenInfo = serializer.Load(outputBytes);\n        var deserializedRoot = projectOpenInfo.Root;\n        var warning = projectOpenInfo.OpenResult.Warnings;\n            \n        // final step, the loading process doesn't save the actual SMC file bytes, so we do it ourselves here\n        deserializedRoot.Project.Data.RomBytes.CopyRomDataIn(romFileBytes);\n\n        // now we can do a full compare between the original project, and the project which has been cycled through\n        // serialization and deserialization\n        warning.Should().BeEmpty(\"there should be no warnings\");\n        deserializedRoot.Project.Data.Labels.Labels.Count().Should().Be(srcProject.Data.Labels.Labels.Count());\n\n        TestEquivalent(x => x.Data.RomBytes, deserializedRoot, srcProject);\n        TestEquivalent(x => x.Data.Comments, deserializedRoot, srcProject);\n        TestEquivalent(x => x.Data.Labels, deserializedRoot, srcProject);\n\n        deserializedRoot.Project.Should().BeEquivalentTo(srcProject);\n\n        deserializedRoot.Project.Data.Comments.Count.Should().BePositive();\n        deserializedRoot.Project.Data.Labels.Labels.Count().Should().BePositive();\n\n        CartNameTests.TestRomCartTitle(deserializedRoot.Project, expectedTitle);\n    }\n\n    private static void TestEquivalent(Func<Project, object> func, ProjectXmlSerializer.Root root, Project project) => \n        func(root.Project).Should().BeEquivalentTo(func(project));\n}"
  },
  {
    "path": "Diz.Test/Tests/SerializationTests/ProjectServicesTests.cs",
    "content": "﻿using System;\nusing Diz.Core;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.project;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing LightInject;\nusing Xunit;\n\nnamespace Diz.Test.Tests.SerializationTests;\n\npublic class ProjectServicesTests : ContainerFixture\n{\n    [Inject] private readonly Func<ILinkedRomBytesProvider> createLinkedProvider = null!;\n    \n    [Fact]\n    public void TestVariousServicesExist()\n    {\n        ServiceFactory.GetInstance<IReadFromFileBytes>();\n        createLinkedProvider().Should().NotBeNull();\n        \n        ServiceFactory.GetAllInstances<IMigrationRunner>().Should().HaveCountGreaterOrEqualTo(1);\n        ServiceFactory.GetInstance<IProjectFactory>();\n        ServiceFactory.GetInstance<IProjectImportSettingsFactory>();\n        ServiceFactory.GetInstance<IProjectImporter>();\n        ServiceFactory.GetInstance<IProjectFileManager>();\n        ServiceFactory.GetInstance<IXmlSerializerFactory>();\n        ServiceFactory.GetInstance<IProjectXmlSerializer>();\n        ServiceFactory.GetInstance<IDataFactory>();\n        ServiceFactory.GetInstance<IFileByteProvider>();\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/SerializationTests/RomByteTests.cs",
    "content": "﻿using Diz.Core;\nusing Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\nusing Diz.Cpu._65816;\nusing Diz.Test.Utils;\nusing Xunit;\n\nnamespace Diz.Test;\n\npublic class RomByteTests : ContainerFixture\n{\n    private static RomByte SampleRomByte1()\n    {\n        return new RomByte\n        {\n            Arch = Architecture.Apuspc700,\n            DataBank = 90,\n            DirectPage = 3,\n            MFlag = true,\n            XFlag = false,\n            TypeFlag = FlagType.Graphics,\n            Point = InOutPoint.InPoint | InOutPoint.ReadPoint,\n            Rom = 0x78,\n        };\n    }\n\n    private static RomByte SampleRomByte2()\n    {\n        // same as above, but just change .Rom\n        var rb = SampleRomByte1();\n        rb.Rom = 0x99;\n        return rb;\n    }\n\n    [Inject] private readonly ISampleDataFactory createSampleData = null!;\n        \n    [Fact]\n    public void TestWhenNoIaPresent()\n    {\n        var sampleData = createSampleData.Create();\n        const int offset = 0x1C1F;\n        var result = sampleData.GetSnesApi()?.GetIntermediateAddressOrPointer(offset);\n        Assert.Equal(result, -1);\n    }\n        \n    [Fact]\n    public void TestGetAddressMode()\n    {\n        var sampleData = createSampleData.Create();\n        const int romOffset1 = 0xEB;\n        var snesApi = sampleData.GetSnesApi();\n        \n        var mode1 = Cpu65C816<ISnesData>.GetAddressMode(snesApi, romOffset1);\n        Assert.Equal(Cpu65C816Constants.AddressMode.Constant8, mode1);\n\n        Assert.True(romOffset1 >= sampleData.GetTag<SampleDataGenerationTag>()!.OriginalRomSizeBeforePadding);\n\n        var mode2 = Cpu65C816<ISnesData>.GetAddressMode(snesApi, 0x0A);\n        Assert.Equal(Cpu65C816Constants.AddressMode.Constant8, mode2);\n    }\n\n    [Fact]\n    public void TestEqualsButNotCompareByte()\n    {\n        var rb1 = SampleRomByte1();\n        var rb2 = SampleRomByte2();\n\n        Assert.True(rb1.EqualsButNoRomByte(rb2));\n        Assert.False(rb1.Equals(rb2));\n\n        rb1.Point = InOutPoint.EndPoint;\n        Assert.False(rb1.EqualsButNoRomByte(rb2));\n        Assert.False(rb1.Equals(rb2));\n    }\n\n    [Fact]\n    public void TestEquals()\n    {\n        var rb1 = SampleRomByte1();\n        var rb2 = SampleRomByte1();\n\n        Assert.True(rb1.Equals(rb2));\n        Assert.True(rb1.EqualsButNoRomByte(rb2));\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/TracelogTests/TraceLogTests.cs",
    "content": "﻿using System.Threading;\nusing Diz.Import.bsnes.tracelog;\nusing Xunit;\n\nnamespace Diz.Test\n{\n    public static class TraceLogTests\n    {\n        const string example1 = @\"028cde rep #$30               A:0004 X:0000 Y:0004 S:1fdd D:0000 DB:02 nvMxdiZC V:133 H: 654 F:36\";\n        const string example2 = @\"c3091a mvn $00,$00            A:001c X:0923 Y:0953 S:06ef D:F001 DB:AF nvmXdIzC V:241 H:  9 F:19\";\n        const string example3 = @\"90c860 sta $420d      [00420d] A:0001 X:0000 Y:0000 S:01ff D:2134 DB:9A ..1B.I.. V:  0 H: 62 F: 0\";\n\n        [Fact]\n        public static void TestCacheLines1()\n        {\n            var cache = new BsnesTraceLogImporter.CachedTraceLineTextIndex();\n            cache.RecomputeCachedIndicesBasedOn(example1);\n\n            // make sure the indices are correct\n            Assert.Equal(0, cache.Addr);\n            Assert.Equal(60, cache.D);\n            Assert.Equal(68, cache.Db);\n            Assert.Equal(71, cache.FN);\n            Assert.Equal(72, cache.FV);\n            Assert.Equal(73, cache.FM);\n            Assert.Equal(74, cache.FX);\n            Assert.Equal(75, cache.FD);\n            Assert.Equal(76, cache.FI);\n            Assert.Equal(77, cache.FZ);\n            Assert.Equal(78, cache.FC);\n            Assert.Equal(cache.Flags, cache.FN);\n            \n            Assert.Equal(cache.LastLineLength, example1.Length);\n        }\n        \n        [Fact]\n        public static void TestCacheLines2()\n        {\n            var cache = new BsnesTraceLogImporter.CachedTraceLineTextIndex();\n            cache.RecomputeCachedIndicesBasedOn(example2);\n\n            // make sure the indices are correct\n            Assert.Equal(0, cache.Addr);\n            Assert.Equal(60, cache.D);\n            Assert.Equal(68, cache.Db);\n            Assert.Equal(71, cache.FN);\n            Assert.Equal(72, cache.FV);\n            Assert.Equal(73, cache.FM);\n            Assert.Equal(74, cache.FX);\n            Assert.Equal(75, cache.FD);\n            Assert.Equal(76, cache.FI);\n            Assert.Equal(77, cache.FZ);\n            Assert.Equal(78, cache.FC);\n            Assert.Equal(cache.Flags, cache.FN);\n            \n            Assert.Equal(cache.LastLineLength, example2.Length);\n            \n            cache.RecomputeCachedIndicesBasedOn(example3);\n            Assert.Equal(cache.LastLineLength, example3.Length);\n        }\n        \n        \n        [Fact]\n        public static void TestCacheLines3()\n        {\n            var cache = new BsnesTraceLogImporter.CachedTraceLineTextIndex();\n            cache.RecomputeCachedIndicesBasedOn(example3);\n\n            // make sure the indices are correct\n            Assert.Equal(0, cache.Addr);\n            Assert.Equal(61, cache.D);\n            Assert.Equal(69, cache.Db);\n            Assert.Equal(72, cache.FN);\n            Assert.Equal(73, cache.FV);\n            Assert.Equal(74, cache.FM);\n            Assert.Equal(75, cache.FX);\n            Assert.Equal(76, cache.FD);\n            Assert.Equal(77, cache.FI);\n            Assert.Equal(78, cache.FZ);\n            Assert.Equal(79, cache.FC);\n            Assert.Equal(cache.Flags, cache.FN);\n        }\n        \n        [Fact]\n        public static void TestParseText1()\n        {\n            var modData = new BsnesTraceLogImporter.ModificationData();\n            var importer = new BsnesTraceLogImporter(null);\n            \n            importer.ParseTextLine(example1, modData);\n            \n            Assert.Equal(0x28cde, modData.SnesAddress);\n            Assert.Equal(0x0000, modData.DirectPage);\n            Assert.Equal(0x02, modData.DataBank);\n            Assert.True(modData.MFlagSet);\n            Assert.False(modData.XFlagSet);\n        }\n        \n        [Fact]\n        public static void TestParseText2()\n        {\n            var modData = new BsnesTraceLogImporter.ModificationData();\n            var importer = new BsnesTraceLogImporter(null);\n            \n            importer.ParseTextLine(example2, modData);\n            Assert.Equal(0xc3091a, modData.SnesAddress);\n            Assert.Equal(0xF001, modData.DirectPage);\n            Assert.Equal(0xAF, modData.DataBank);\n            Assert.False(modData.MFlagSet);\n            Assert.True(modData.XFlagSet);\n        }\n        \n        [Fact]\n        public static void TestParseText3()\n        {\n            var modData = new BsnesTraceLogImporter.ModificationData();\n            var importer = new BsnesTraceLogImporter(null);\n            \n            importer.ParseTextLine(example3, modData);\n\n            Assert.Equal(0x90c860, modData.SnesAddress);\n            Assert.Equal(0x2134, modData.DirectPage);\n            Assert.Equal(0x9A, modData.DataBank);\n            Assert.True(modData.MFlagSet);\n            Assert.True(modData.XFlagSet);\n        }\n    }\n}\n\n"
  },
  {
    "path": "Diz.Test/Tests/UtilsTests/OptimizedHexConversionTests.cs",
    "content": "﻿using System;\nusing System.IO;\nusing Diz.Core.util;\nusing Xunit;\n\nnamespace Diz.Test\n{\n    public static class OptimizedHexConversionTests\n    {\n        [Fact]\n        public static void TestHex1()\n        {\n            Assert.Equal(0xF, ByteUtil.ByteParseHex1('F'));\n        }\n\n        private const string validHexChars = \"0123456789ABCDEF\";\n        \n        [Fact]\n        public static void TestHexRange()\n        {\n            for (var c = '\\0'; c < 255; ++c)\n            {\n                if (validHexChars.Contains(char.ToUpper(c)))\n                {\n                    Assert.Equal(Convert.ToInt32(c.ToString(), 16), ByteUtil.ByteParseHex1(c));\n                }\n                else\n                {\n                    Assert.Throws<InvalidDataException>(() => ByteUtil.ByteParseHex1(c));    \n                }\n                \n            }\n        }\n\n        [Fact]\n        public static void TestHex2()\n        {\n            Assert.Equal(0xF0, ByteUtil.ByteParseHex2('F', '0'));\n        }\n\n        [Fact]\n        public static void TestHex4()\n        {\n            Assert.Equal((uint)0xF029, ByteUtil.ByteParseHex4('F', '0', '2', '9'));\n        }\n\n        [Fact]\n        public static void TestHexM()\n        {\n            Assert.Equal((uint)0xF029, ByteUtil.ByteParseHex(\"F029\", 0, 4));\n            Assert.Equal((uint)0xF02, ByteUtil.ByteParseHex(\"F02\", 0, 3));\n            Assert.Equal((uint)0xF0, ByteUtil.ByteParseHex(\"F0\", 0, 2));\n            Assert.Equal((uint)0xF, ByteUtil.ByteParseHex(\"F\", 0, 1));\n            \n            Assert.Equal((uint)0xF, ByteUtil.ByteParseHex(\" F\", 1, 1));\n            Assert.Equal((uint)0xF2, ByteUtil.ByteParseHex(\" F2 \", 1, 2));\n            Assert.Equal((uint)0xF029, ByteUtil.ByteParseHex(\" F0297\", 1, 4));\n            Assert.Equal((uint)0xF02979A, ByteUtil.ByteParseHex(\" F02979A\", 1, 7));\n            Assert.Equal(0xF02979A9, ByteUtil.ByteParseHex(\" F02979A9\", 1, 8));\n            \n            Assert.Throws<ArgumentException>(() => ByteUtil.ByteParseHex(\"F02988554324AC\", 0, 9));\n            Assert.Throws<ArgumentException>(() => ByteUtil.ByteParseHex(\"F029\", 0, 0));\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Tests/UtilsTests/SuperFamiCheckTests.cs",
    "content": "﻿using System.Collections.Generic;\nusing Diz.Test.Utils.SuperFamiCheckUtil;\nusing FluentAssertions;\nusing Sprache;\nusing Xunit;\n\nnamespace Diz.Test\n{\n    public class SuperFamiCheckTests\n    {\n        public static IEnumerable<object[]> TestHexData => new List<object[]>\n        {\n            new object[] { \"0x134572AB\", 0x134572AB },\n            new object[] { \"0xFFFF\", 0xFFFF },\n            new object[] { \"FFFF\", 0xFFFF },\n            new object[] { \"0x01ABF\", 0x01ABF },\n            new object[] { \"AABBCCDD\", 0xAABBCCDD },\n        };\n        \n        public static IEnumerable<object[]> InvalidHexData => new List<object[]>\n        {\n            new object[] { \"0x000000000000\" },\n            new object[] { \"0x000000000001\" },\n            new object[] { \"asdf\" },\n            new object[] { \"0x9999-\" },\n            new object[] { \"x34572AB\" },\n            new object[] { \"99999999FG\" },\n            new object[] { \"AABBCCDDE\"},\n        };\n\n\n        [Theory]\n        [MemberData(nameof(InvalidHexData))]\n        public static void TestHexParseInvalid(string invalidHexData)\n        {\n            DizSuperFamiCheckParse.HexNumber.End().Invoking(x => x.Parse(invalidHexData))\n                .Should().Throw<ParseException>();\n        }\n        \n        [Theory]\n        [MemberData(nameof(TestHexData))]\n        public static void TestHexParse(string hexInput, uint expectedOutput)\n        {\n            var num = DizSuperFamiCheckParse.HexNumber.End().Parse(hexInput);\n            num.Should().Be(expectedOutput);\n        }\n\n        [Fact]\n        public static void TestOneLine()\n        {\n            const string input = \"         Checksum    0x788c            \";\n            \n            var output = \n                DizSuperFamiCheckParse.ParseKvpLine(input)\n                    .Should()\n                    .Be((\"Checksum\", 0x788c));\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/AppServicesForTests.cs",
    "content": "﻿namespace Diz.Test.Utils;"
  },
  {
    "path": "Diz.Test/Utils/AsarRunner.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\n\nnamespace Diz.Test.Utils\n{\n    public static class AsarRunner\n    {\n        private const string PathToAsar = @\"D:\\projects\\cthack\\src\\rom\\bin\\asar-domfix.exe\";\n\n        //  --no-title-check\n        \n        public static IReadOnlyList<byte> AssembleToRom(string assemblyCode)\n        {\n            var tmpOutputAsm = Path.GetTempPath() + Guid.NewGuid().ToString() + \".asm\";\n            var tmpOutputRom = Path.GetTempPath() + Guid.NewGuid().ToString() + \".sfc\";\n            \n            using var swAsm = new StreamWriter(\n                new FileStream(tmpOutputAsm, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 4096));\n            \n            swAsm.Write(assemblyCode);\n\n            swAsm.Close(); swAsm.Dispose();\n\n            var psi = new ProcessStartInfo(PathToAsar)\n            {\n                WorkingDirectory = Path.GetDirectoryName(PathToAsar) ?? \"\",\n                Arguments = $\"\\\"{tmpOutputAsm}\\\" \\\"{tmpOutputRom}\\\"\"\n            };\n            Process.Start(psi)?.WaitForExit(20000);\n\n            return File.ReadAllBytes(tmpOutputRom);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/AssemblyPipelineTester.cs",
    "content": "﻿using Diz.Core.model.snes;\n\nnamespace Diz.Test.Utils\n{\n    public class AssemblyPipelineTester\n    {\n        public Data Source { get; set; }\n        public string ExpectedAsmOutput { get; set; }\n\n        public virtual void Test()\n        {\n            var outputAsmToTest = LogWriterHelper.ExportAssembly(Source);\n            LogWriterHelper.AssertAssemblyOutputEquals(ExpectedAsmOutput, outputAsmToTest);\n        }\n\n        public static AssemblyPipelineTester SetupFromResource(Data input, string expectedAsmOutputResource)\n        {\n            return new AssemblyPipelineTester\n            {\n                Source = input,\n                ExpectedAsmOutput = EmbeddedResourceDataAttribute.ReadResource(expectedAsmOutputResource)\n            };\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/BenchmarkXunitLogger.cs",
    "content": "﻿using BenchmarkDotNet.Configs;\nusing BenchmarkDotNet.Running;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test.Utils\n{\n    public static class XunitBenchmark\n    {\n        public static void Run<T>(ITestOutputHelper xUnitOutput, bool debugOk=false)\n        {\n            var logger = new XOutLogger(xUnitOutput);\n            var config = ManualConfig.Create(DefaultConfig.Instance);\n            config.AddLogger(logger);\n\n            // don't use this for capturing performance info. just for debugging harnesses.\n            // always run benchmarks in release mode.\n            if (debugOk)\n                config.Options &= ConfigOptions.DisableOptimizationsValidator;\n\n            BenchmarkRunner.Run<T>(config);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/CorrectingRangeTests.cs",
    "content": "﻿using Diz.Core.util;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Diz.Test.Tests.UtilsTests\n{\n    public class CorrectingRangeTests\n    {\n        [Fact]\n        public void TestSomeBasics()\n        {\n            var range = new CorrectingRange(100) {StartIndex = 2, RangeCount = 10};\n            range.EndIndex.Should().Be(11);\n\n            range.EndIndex = 11;\n            range.RangeCount.Should().Be(10);\n\n            range.EndIndex = 99;\n            range.StartIndex.Should().Be(100 - 10);\n            range.RangeCount.Should().Be(10);\n\n            range.StartIndex = 95;\n            range.RangeCount.Should().Be(5);\n\n            range.StartIndex = -1;\n            range.StartIndex.Should().Be(0);\n            range.EndIndex.Should().Be(5 - 1);\n            \n            range.StartIndex = 101;\n            range.StartIndex.Should().Be(99);\n        }\n\n        [Fact]\n        public void TestInvalid()\n        {\n            var range = new CorrectingRange(100);\n            range.EndIndex.Should().Be(-1);\n            range.RangeCount.Should().Be(0);\n            range.StartIndex.Should().Be(0);\n\n            range.RangeCount = 1;\n            range.EndIndex.Should().Be(0);\n            range.RangeCount.Should().Be(1);\n            range.StartIndex.Should().Be(0);\n            \n            range.RangeCount = 10;\n            range.EndIndex.Should().Be(9);\n            range.RangeCount.Should().Be(10);\n            range.StartIndex.Should().Be(0);\n        }\n\n        [Fact]\n        public void TestCombos()\n        {\n            new CorrectingRange(100) {StartIndex = 2, RangeCount = 10};\n            new CorrectingRange(100) {StartIndex = 2, EndIndex = 20, RangeCount = 10};\n            new CorrectingRange(100) {EndIndex = 20, RangeCount = 10, StartIndex = 2};\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/DizTestCompositionRoot.cs",
    "content": "﻿using LightInject;\n\nnamespace Diz.Test.Utils;\n\npublic class DizTestCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        // none should really be needed, but, add anything test-specific if needed. use sparingly.\n        // consider overriding the Configure() method in ContainerFixture instead\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/EmbeddedResourceDataAttribute.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing Diz.Core.util;\nusing Xunit.Sdk;\n\nnamespace Diz.Test.Utils\n{\n    // based on https://patriksvensson.se/2017/11/using-embedded-resources-in-xunit-tests/\n    public sealed class EmbeddedResourceDataAttribute : DataAttribute\n    {\n        private readonly string[] resourcesToRead;\n        \n        public EmbeddedResourceDataAttribute(params string[] resourcesToRead)\n        {\n            this.resourcesToRead = resourcesToRead;\n        }\n\n        public static string ReadResource(string resource)\n        {\n            var assembly = typeof(EmbeddedResourceDataAttribute).GetTypeInfo().Assembly;\n            return Util.ReadManifestData(assembly, resource);\n        } \n\n        public override IEnumerable<object[]> GetData(MethodInfo testMethod)\n        {\n            return resourcesToRead\n                .Select(ReadResource)\n                .Select(resource => new[] {resource});\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/EmptyRomTestData.cs",
    "content": "﻿using Diz.Core.Interfaces;\nusing Diz.Core.model;\nusing Diz.Core.model.snes;\n\nnamespace Diz.Test.Utils;\n\npublic class EmptyRom : Data\n{\n    public EmptyRom()\n    {\n        RomMapMode = RomMapMode.LoRom;\n        RomSpeed = RomSpeed.FastRom;\n\n        // note: slow.\n        while (RomBytes.Count < 0xFFFF * 64)\n            RomBytes.Add(new RomByte());\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/ExternalRunners.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.IO;\nusing Xunit;\n\nnamespace Diz.Test.Utils;\n\npublic interface ISkippableTest\n{\n    string Skip { get; set; }\n} \n\npublic static class DependentFileChecker\n{\n    public static void SkipUnlessFilesExist(this ISkippableTest @this, string[] files) => \n        @this.Skip ??= CheckExists(files);\n    \n    private static string CheckExists(string[] files = null)\n    {\n        var toCheck = new List<string>();\n        if (files != null)\n            toCheck.AddRange(files);\n        \n        var missingFile = toCheck.Find(f => !File.Exists(f));\n        return missingFile != null ? $\"Can't find test prerequisite file {missingFile}\" : null;\n    }\n}\n\npublic class FactOnlyIfFilePresent : FactAttribute, ISkippableTest\n{\n    public FactOnlyIfFilePresent(string[] files = null) => \n        this.SkipUnlessFilesExist(files);\n}\n\npublic sealed class TheoryOnlyIfFilePresent : TheoryAttribute, ISkippableTest\n{\n    public TheoryOnlyIfFilePresent(string[] files = null) => \n        this.SkipUnlessFilesExist(files);\n}"
  },
  {
    "path": "Diz.Test/Utils/ExternalToolRunner.cs",
    "content": "﻿using System;\nusing Diz.Core.util;\n\nnamespace Diz.Test.Utils\n{\n    public class ExternalToolRunner\n    {\n        private string exeFilename;\n        public ExternalToolRunner(string exeFilename)\n        {\n            this.exeFilename = exeFilename;\n        }\n\n        // some tools may need this false\n        public bool ThrowIfAnythingOnStderr { get; set; } = true;\n\n        public string RunCommand(string args)\n        {\n            var (stdout, stderr) = Util.RunCommandGetOutput(exeFilename, args);\n\n            if (ThrowIfAnythingOnStderr && !string.IsNullOrEmpty(stderr))\n                throw new InvalidOperationException($\"Tool run failed, stderr present: {stderr}\");\n\n            return stdout;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/LogWriterHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing Diz.Core.export;\nusing Diz.Core.model.snes;\nusing Diz.LogWriter;\nusing Diz.LogWriter.util;\nusing FluentAssertions;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test.Utils\n{\n    public static class LogWriterHelper\n    {\n        public class ParsedOutput\n        {\n            private bool Equals(ParsedOutput other)\n            {\n                return Label == other.Label && Instr == other.Instr && CommentPc == other.CommentPc && CommentRawBytes == other.CommentRawBytes && CommentIa == other.CommentIa && CommentActualText == other.CommentActualText;\n            }\n\n            public override bool Equals(object obj)\n            {\n                if (ReferenceEquals(null, obj)) return false;\n                if (ReferenceEquals(this, obj)) return true;\n                if (obj.GetType() != GetType()) return false;\n                return Equals((ParsedOutput) obj);\n            }\n\n            public override int GetHashCode()\n            {\n                unchecked\n                {\n                    var hashCode = Label.GetHashCode();\n                    hashCode = (hashCode * 397) ^ Instr.GetHashCode();\n                    hashCode = (hashCode * 397) ^ CommentPc.GetHashCode();\n                    hashCode = (hashCode * 397) ^ CommentRawBytes.GetHashCode();\n                    hashCode = (hashCode * 397) ^ CommentIa.GetHashCode();\n                    hashCode = (hashCode * 397) ^ CommentActualText.GetHashCode();\n                    return hashCode;\n                }\n            }\n\n            public string Label;\n            public string Instr;\n\n            public string CommentPc;\n            public string CommentRawBytes;\n            public string CommentIa;\n\n            public string CommentActualText;\n        }\n\n        private static ParsedOutput ParseLine(string line)\n        {\n            if (line == \"\")\n                return null;\n\n            var output = new ParsedOutput();\n\n            var split = line.Split([';'], 2,options:StringSplitOptions.None);\n            var main = split[0].Trim(); \n            var comment = split[1].Trim();\n\n            // parse the stuff in the comment (it may or may not be there)\n            var csplit = comment.Split(['|'], 3, options: StringSplitOptions.None);\n            if (csplit.Length <= 3)\n            {\n                output.CommentActualText = comment;\n            }\n            else\n            {\n                output.CommentPc = csplit[0].Trim();\n                output.CommentRawBytes = csplit[1].Trim();\n\n                var iasplit = csplit[2].Split([';'], 2, options: StringSplitOptions.None);\n                output.CommentIa = iasplit[0].Trim();\n                output.CommentActualText = iasplit[1].Trim();\n            }\n\n            var msplit = main.Split([':'], 2, options: StringSplitOptions.None);\n            var m1 = msplit[0].Trim();\n            var m2  = msplit.Length > 1 ? msplit[1].Trim() : \"\";\n\n            if (m2 != \"\")\n            {\n                output.Label = m1;\n                output.Instr = m2;\n            }\n            else\n            {\n                output.Label = \"\";\n                output.Instr = m1;\n            }\n\n            return output;\n        }\n\n        private static List<ParsedOutput> ParseAll(string lines) =>\n            lines.Split(new[] {'\\n'})\n                .Select(line => ParseLine(line.Trim()))\n                .ToList();\n\n        public static void AssertAssemblyOutputEquals(string expectedRaw, LogCreatorOutput.OutputResult result, ITestOutputHelper testOutputHelper = null)\n        {\n            testOutputHelper?.WriteLine(\"** EXPECTED **\");\n            testOutputHelper?.WriteLine(expectedRaw);\n            \n            testOutputHelper?.WriteLine(\"** ACTUAL **\");\n            testOutputHelper?.WriteLine(result.AssemblyOutputStr);\n            \n            AssertGoodOutput(result);\n            \n            // parse the output so we can better pinpoint where errors are\n            var expectedOut = ParseAll(expectedRaw);\n            var actualOut = ParseAll(result.AssemblyOutputStr);\n            AssertAssemblyOutputEqual(expectedOut, actualOut);\n            \n            // now that the parsed version passed, compare the raw strings\n            // if you hit this and not the above section, your whitespace or newline [CRLF vs LF] might be off.\n            result.AssemblyOutputStr.Should().Be(expectedRaw);\n        }\n\n        public static LogCreatorOutput.OutputResult ExportAssembly(Data inputRom, Action<LogCreator> postInitHook = null)\n        {\n            var logCreator = new LogCreator\n            {\n                Data = new LogCreatorByteSource(inputRom),\n                Settings = new LogWriterSettings {\n                    OutputToString = true,\n                    Structure = LogWriterSettings.FormatStructure.SingleFile,\n                    SuppressSingleFileModeDisabledError = true, // hack for now\n                }\n            };\n            \n            postInitHook?.Invoke(logCreator);\n\n            return logCreator.CreateLog();\n        }\n\n        [SuppressMessage(\"ReSharper\", \"ParameterOnlyUsedForPreconditionCheck.Local\")]\n        private static void AssertGoodOutput(LogCreatorOutput.OutputResult result)\n        {\n            Assert.True(result.LogCreator != null);\n            Assert.True(result.AssemblyOutputStr != null);\n            Assert.True(result.ErrorCount == 0);\n        }\n\n        private static void AssertAssemblyOutputEqual(IReadOnlyList<ParsedOutput> expectedOut, IReadOnlyList<ParsedOutput> actualOut)\n        {\n            TestUtil.AssertCollectionEqual(expectedOut, actualOut);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/ServiceContainerFixture.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Diz.Core.services;\nusing Diz.Core.util;\nusing LightInject;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test.Utils;\n\n// if tagged with this, inject this field\n// must be a private field on a class derived from ContainerFixture\n[AttributeUsage(AttributeTargets.Field)]\npublic class Inject : Attribute\n{\n    \n}\n\n// based on sample code from https://github.com/seesharper/LightInject under the \"Unit Testing\" section\n// inject services into any private fields on derived classes\npublic class ContainerFixture : IDisposable\n{\n    private readonly bool injectFieldsOnlyIfNull;\n    private readonly bool injectOnlyTaggedFields;\n\n    [SuppressMessage(\"ReSharper\", \"VirtualMemberCallInConstructor\")]\n    public ContainerFixture(bool injectFieldsOnlyIfNull = true, bool injectOnlyTaggedFields = true)\n    {\n        this.injectFieldsOnlyIfNull = injectFieldsOnlyIfNull;\n        this.injectOnlyTaggedFields = injectOnlyTaggedFields;\n        var container = ConfigureAndRegisterServiceContainer();\n        ServiceFactory = container.BeginScope();\n        InjectPrivateFields();\n    }\n\n    public virtual IServiceContainer ConfigureAndRegisterServiceContainer()\n    {\n        var container = CreateContainer();\n        Configure(container);\n        RegisterServices(container);\n        return container;\n    }\n\n    private static IServiceContainer RegisterServices(IServiceContainer container)\n    {\n        DizCoreServicesDllRegistration.RegisterServicesInDizDlls(container);\n        return container;\n    }\n\n    private void InjectPrivateFields()\n    {\n        var privateInstanceFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);\n        foreach (var privateInstanceField in privateInstanceFields)\n        {\n            if (injectFieldsOnlyIfNull && privateInstanceField.GetValue(this) != null)\n                continue;\n            \n            if (injectOnlyTaggedFields && !Attribute.IsDefined(privateInstanceField, typeof(Inject)))\n                continue;\n            \n            privateInstanceField.SetValue(this, GetInstance(ServiceFactory, privateInstanceField));\n        }\n    }\n\n    internal Scope ServiceFactory { get; }\n\n    public void Dispose() => ServiceFactory.Dispose();\n\n    public TService GetInstance<TService>(string name = \"\")\n        => ServiceFactory.GetInstance<TService>(name);\n\n    private object GetInstance(IServiceFactory factory, FieldInfo field)\n    {\n        // skip this type of interface so Xunit can populate it:\n        if (field.FieldType.IsAssignableTo(typeof(ITestOutputHelper)))\n            return null;\n\n        return ServiceFactory.TryGetInstance(field.FieldType) ??\n               ServiceFactory.GetInstance(field.FieldType, field.Name);\n    }\n\n    internal virtual IServiceContainer CreateContainer() => \n        CreateServiceContainer();\n\n    public static IServiceContainer CreateServiceContainer() => \n        DizServiceProvider.CreateServiceContainer();\n\n    protected virtual void Configure(IServiceRegistry serviceRegistry) {}\n    \n    protected static IServiceContainer CreateAndRegisterServiceContainer() => \n        RegisterServices(CreateServiceContainer());\n}"
  },
  {
    "path": "Diz.Test/Utils/SuperFamiCheckUtil/DizSuperFamiCheckParse.cs",
    "content": "﻿using System;\nusing Sprache;\n\nnamespace Diz.Test.Utils.SuperFamiCheckUtil\n{\n    public static class DizSuperFamiCheckParse\n    {\n        public static readonly Parser<(string key, uint value)> Kvp =\n            from leading in Parse.WhiteSpace.Many()\n            from key in Identifier.Token()\n            from value in HexNumber.Token()\n            select (key, value);\n\n        public static readonly Parser<string> Identifier =\n            from leading in Parse.WhiteSpace.Many()\n            from id in Parse.Letter.Many().Text()\n            from trailing in Parse.WhiteSpace.Many()\n            select id;\n\n        public static readonly Parser<uint> HexNumber =\n            from prefix in Parse.String(\"0x\")\n                .Once().Optional()\n            from numericString in Parse.Digit\n                .Or(Parse.Chars(\"abcdefABCDEF\"))\n                .Repeat(1, 8).Text()\n            select Convert.ToUInt32(numericString, 16);\n\n        public static (string key, uint value) ParseKvpLine(string input) =>\n            Kvp.End().Parse(input);\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/SuperFamiCheckUtil/SuperFamiCheck.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace Diz.Test.Utils.SuperFamiCheckUtil\n{\n    public class SuperFamiCheckTool : ExternalToolRunner\n    {\n        public const string Exe = @\"D:\\projects\\DiztinGUIsh-main\\Diz.Test\\external-tools\\superfamicheck.exe\";\n\n        public SuperFamiCheckTool() : base(Exe) {}\n\n        public struct Result\n        {\n            public uint Checksum { get; set; }\n            public uint Complement { get; set; }\n            public uint AllCheckBytes => (Checksum << 16) | Complement;\n        }\n        \n        public static Result Run(string romName) => new SuperFamiCheckTool().RunInternal(romName);\n\n        private Result RunInternal(string romName)\n        {\n            var output = RunAndGetOutput(romName);\n            var checksumLine = output.Find(x => x.Contains(\"Checksum\"));\n            var complementLine = output.Find(x => x.Contains(\"Complement\"));\n\n            var (checksumTxt, checksum) = DizSuperFamiCheckParse.ParseKvpLine(checksumLine);\n            var (complementTxt, complement) = DizSuperFamiCheckParse.ParseKvpLine(complementLine);\n\n            if (checksumTxt != \"Checksum\" || complementTxt != \"Complement\")\n                throw new InvalidDataException(\"Couldn't parse output from SuperFamiCheck\");\n\n            return new Result\n            {\n                Checksum = checksum,\n                Complement = complement\n            };\n        }\n\n        private List<string> RunAndGetOutput(string romName)\n        {\n            var escapedFilename = $\"\\\"{romName}\\\"\";\n            return RunCommand(escapedFilename).Split('\\n').ToList();\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/TestUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Diz.Core.model;\nusing Diz.Core.model.project;\nusing Diz.Cpu._65816;\nusing FluentAssertions;\nusing Moq;\nusing Xunit;\n\nnamespace Diz.Test.Utils;\n\n\npublic interface ISampleRomTestData\n{\n    byte[] SampleRomBytes { get; }\n    public Project Project { get; }\n}\n\npublic class SampleRomTestDataFixture : ContainerFixture, ISampleRomTestData\n{\n    public byte[] SampleRomBytes => sampleBytes.Value;\n    \n    [Inject] private readonly ISnesSampleProjectFactory sampleFactory = null!;\n    private readonly Lazy<byte[]> sampleBytes;\n    public SampleRomTestDataFixture()\n    {\n        sampleBytes = new Lazy<byte[]>(() =>\n        {\n            Project = sampleFactory!.Create() as Project;\n            return Project!.Data.GetFileBytes().ToArray();\n        });\n    }\n\n    public Project Project { get; set; }\n}\n\n\npublic static class TheoryDataGenerator\n{\n    public static TheoryData<T> CreateTheoryData<T>(this IEnumerable<Func<T>> data)\n    {\n        return data\n            .Select(fn => fn())\n            .Aggregate(new TheoryData<T>(), (theoryData, item) =>\n            {\n                theoryData.Add(item);\n                return theoryData;\n            });\n    }\n}\n    \npublic static class TestUtil\n{\n    public static void AssertCollectionEqual<T>(IReadOnlyList<T> expected, IReadOnlyList<T> actual)\n    {\n        // do some weirdness here to better display the differences in the output window.\n            \n        var largestListCount = Math.Max(expected.Count, actual.Count);\n        for (var i = 0; i < largestListCount; ++i)\n        {\n            // if this gets hit, lengths of lists are different\n            (i < actual.Count).Should().BeTrue();\n            (i < expected.Count).Should().BeTrue();\n\n            var expectedItem = expected[i];\n            var actualItem = actual[i];\n                \n            expectedItem.Should().BeEquivalentTo(actualItem);\n        }\n            \n        expected.Count.Should().Be(actual.Count);\n\n        expected.Should().BeEquivalentTo(actual);\n    }\n\n    public static Mock<IReadFromFileBytes> CreateReadFromFileMock(byte[] mockedFileBytes)\n    {\n        var mockLinkedRomBytesProvider = new Mock<IReadFromFileBytes>();\n        mockLinkedRomBytesProvider.Setup(x =>\n                x.ReadRomFileBytes(It.IsAny<string>()))\n            .Returns<string>(filename => mockedFileBytes);\n\n        return mockLinkedRomBytesProvider;\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/XmlTestUtilBase.cs",
    "content": "﻿using Diz.Test.Utils;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test.Tests.SerializationTests\n{\n    public abstract class XmlTestUtilBase\n    {\n        public readonly ITestOutputHelper TestOutputHelper;\n        public XmlTestUtils XmlTestUtils => new() {Output = TestOutputHelper}; \n        public XmlTestUtilBase(ITestOutputHelper testOutputHelper)\n        {\n            TestOutputHelper = testOutputHelper;\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/Utils/XmlTestUtils.cs",
    "content": "﻿using System;\nusing Xunit;\nusing Xunit.Abstractions;\n\nnamespace Diz.Test.Utils\n{\n    public class XmlTestUtils\n    {\n        public ITestOutputHelper Output { get; set; }\n        \n        public void RunFullCycle<T>(Func<T> createFn, out T expectedCopy, out T deserializedObj)\n        {\n            RunFullCycleObj(() => createFn(), out var expectedObjCopy, out var deserializedObjCopy);\n\n            expectedCopy = (T)expectedObjCopy;\n            deserializedObj = (T) deserializedObjCopy;\n        }\n\n        public void RunFullCycleObj(Func<object> createFn, out object expectedCopy, out object deserializedObj)\n        {\n            var objToCycle = createFn();\n            expectedCopy = createFn();\n            \n            deserializedObj = XmlFullCycle(objToCycle);\n        }\n        \n        public T XmlFullCycle<T>(T objToCycle)\n        {\n            throw new NotImplementedException();\n            \n            // setup. use InjectData now instead of this mess\n            // // TODO: need to inject SNES api, something vaguely like....\n            // var dataFactoryMock = new Mock<IProjectDataFactory>();\n            // dataFactoryMock.Setup(x => x.Create()).Returns(new Data());\n            // var data = dataFactoryMock.Object;\n            //\n            // var factory = new XmlSerializerFactory(dataFactoryMock,\n            //     () => new XmlSerializerFactory.SnesDataInterceptor(this));\n            \n            // actual test below:\n            // var xmlToCycle = XmlSerializerFactory.GetSerializer().Create().Serialize(objToCycle);\n            // Output?.WriteLine(xmlToCycle);\n            // var deserialized = XmlSerializerFactory.GetSerializer().Create().Deserialize<T>(xmlToCycle);\n            // return deserialized;\n        }\n        \n        public void RunFullCycle(Func<object> createFn)\n        {\n            RunFullCycle(createFn, out var unchanged, out var cycled);\n            Assert.Equal(unchanged, cycled);\n        }\n    }\n}"
  },
  {
    "path": "Diz.Test/app.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n    </assemblyBinding>\n  </runtime>\n  <system.codedom>\n    <compilers>\n      <compiler language=\"c#;cs;csharp\" extension=\".cs\" warningLevel=\"4\" compilerOptions=\"/langversion:default /nowarn:1659;1699;1701\" type=\"Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\">\n        <providerOption name=\"UseAspNetSettings\" value=\"false\" />\n      </compiler>\n      <compiler language=\"vb;vbs;visualbasic;vbscript\" extension=\".vb\" warningLevel=\"4\" compilerOptions=\"/langversion:default /nowarn:41008 /optionInfer+\" type=\"Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\">\n        <providerOption name=\"UseAspNetSettings\" value=\"false\" />\n      </compiler>\n    </compilers>\n  </system.codedom>\n</configuration>"
  },
  {
    "path": "Diz.Test/bugs/Bug050_JapaneseText.cs",
    "content": "﻿#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing Diz.Core.model;\nusing Diz.Core.model.project;\nusing Diz.Core.serialization;\nusing Diz.Core.serialization.xml_serializer;\nusing Diz.Core.util;\nusing Diz.Cpu._65816;\nusing Diz.Test.Utils;\nusing FluentAssertions;\nusing LightInject;\nusing Xunit;\nusing IFileByteProvider = Diz.Core.serialization.IFileByteProvider;\n\nnamespace Diz.Test.bugs;\n\npublic class Bug50ProjectFileManager : ProjectFileManager\n{\n    public event IProjectXmlSerializer.SerializeEvent? BeforeSerialize;\n    public event IProjectXmlSerializer.SerializeEvent? AfterDeserialize;\n\n    protected override ProjectOpenResult DeserializeWith(IProjectSerializer serializer, byte[] rawBytes)\n    {\n        if (serializer is IProjectXmlSerializer xmlSerializer && AfterDeserialize != null)\n            xmlSerializer.AfterDeserialize += AfterDeserialize;\n\n        return base.DeserializeWith(serializer, rawBytes);\n    }\n\n    protected override byte[] SerializeWith(Project project, IProjectSerializer serializer)\n    {\n        var xmlSerializer = serializer as ProjectXmlSerializer;\n        xmlSerializer!.BeforeSerialize += BeforeSerialize;\n                \n        return base.SerializeWith(project, serializer);\n    }\n        \n    public Bug50ProjectFileManager(\n        Func<IProjectXmlSerializer> projectXmlSerializerCreate,\n        Func<IAddRomDataCommand> addRomDataCommandCreate,\n        Func<string, IFileByteProvider> fileByteProviderFactory) \n        : base(\n            projectXmlSerializerCreate, \n            addRomDataCommandCreate,\n            fileByteProviderFactory)\n    {\n    }\n}\n\n// https://github.com/Dotsarecool/DiztinGUIsh/issues/50\npublic class Bug050JapaneseText\n{\n    public class Bug050Fixture : ContainerFixture\n    {\n        // these will be populated by dependency injection\n        [Inject] private readonly ISnesSampleProjectFactory sampleProjectFactory = null!;\n        [Inject] private readonly Func<IProjectXmlSerializer> fnProjectSerializerCreate = null!;\n        [Inject] private readonly Func<IAddRomDataCommand> fnAddRomDataCommand = null!;\n        \n        // generated\n        internal readonly bool ExpectedCartTitleValidationException;\n        internal readonly Project ProjectToSerialize;\n        internal readonly string OriginalGoodGameName;\n        internal readonly string? BadGameName = null;\n        internal readonly int SaveVersionToUse;\n        internal readonly Bug50ProjectFileManager ProjectFileManager;\n        internal readonly FileIoFixture FileIo;\n\n        // populated just before serialization\n        internal string? BeforeDeserializeInternalRomGameName;\n        internal string? BeforeSerializeCartTitleName;\n        \n        // populated after deserialization\n        internal int DeserializedSaveVersionBeforeMigrations = -1;\n        internal string? DeserializedInternalRomGameNameBeforeMigrations;\n        \n        internal Project? DeserializedProject = null;\n        internal ProjectOpenResult? ProjectDeserializedResult = null;\n\n        public Bug050Fixture(\n            bool forceOlderVersion100,\n            string? overrideGameName,\n            bool expectedCartTitleValidationException) : \n            base(injectFieldsOnlyIfNull: true, injectOnlyTaggedFields: true)\n        {\n            FileIo = new FileIoFixture();\n\n            ProjectFileManager = new Bug50ProjectFileManager(\n                fnProjectSerializerCreate, \n                fnAddRomDataCommand, \n                _ => FileIo.Mock.Object\n            );\n            \n            ExpectedCartTitleValidationException = expectedCartTitleValidationException;\n            SaveVersionToUse = forceOlderVersion100 ? 100 : 101;\n            \n            ProjectToSerialize = (sampleProjectFactory.Create() as Project)!;\n            OriginalGoodGameName = ProjectToSerialize.InternalRomGameName;\n            BadGameName = overrideGameName == null ? null : \n                ByteUtil.ReadShiftJisEncodedString(\n                    ByteUtil.PadCartridgeTitleBytes(\n                        ByteUtil.ConvertUtf8ToShiftJisEncodedBytes(overrideGameName)\n                    )\n                );\n\n            // TODO\n            // if (ExpectedCartTitleValidationException)\n            // {\n            //     ProjectFileManager.RomPromptFn = _ =>\n            //         throw new InvalidDataException(\"We should not hit this for this test, if it worked.\");\n            // }\n\n            ProjectFileManager.BeforeSerialize += (serializer, rootElement) =>\n            {\n                // force the save data we're about use to be a specific version, mitigations will only run at v100 and that's most of what we're testing\n                rootElement.SaveVersion = SaveVersionToUse;\n\n                // if requested, intentionally break the cached cart name data by setting it to an invalid value.\n                // this will be serialized, and on deserialized, the migration code should detect this as a previous serialization bug and fix it.\n                if (!string.IsNullOrEmpty(BadGameName))\n                    rootElement.Project.InternalRomGameName = BadGameName;\n\n                // store these for testing later\n                var snesApi = rootElement.Project.Data.GetSnesApi();\n                BeforeDeserializeInternalRomGameName = rootElement.Project.InternalRomGameName;\n                BeforeSerializeCartTitleName = snesApi?.CartridgeTitleName;\n            };\n            \n            ProjectFileManager.AfterDeserialize += (serializer, root) =>\n            {\n                // save some info for later\n                DeserializedSaveVersionBeforeMigrations = root.SaveVersion;\n                DeserializedInternalRomGameNameBeforeMigrations = root.Project.InternalRomGameName;\n            };\n            \n            GetInstance<IReadFromFileBytes>().Should().Match(o => o.GetType().Name.Contains(\"Proxy\"));\n\n            // SUT\n            ProjectFileManager.Save(ProjectToSerialize, \"IGNORED\");\n            var runDeserializerFn = () => ProjectFileManager.Open(\"IGNORED\");\n            var functionAssertions = runDeserializerFn.Invoking(a => ProjectDeserializedResult = a());\n            if (ExpectedCartTitleValidationException)\n            {\n                // the original bug would trigger an exception on failed verification. make sure that is still happening\n                functionAssertions.Should().Throw<InvalidOperationException>().WithMessage(\"Search failed, *\");\n                return;\n            }\n\n            // this is what it looks like when either the bug is is fixed, or things are normal\n            functionAssertions.Should()\n                .NotThrow(\"Didn't expect this to throw because serialized data shouldn't be broken\");\n                \n            DeserializedProject = ProjectDeserializedResult?.Root?.Project;\n            Assert.True(DeserializedProject != null);\n        }\n        \n        protected override void Configure(IServiceRegistry serviceRegistry)\n        {\n            base.Configure(serviceRegistry);\n\n            serviceRegistry.Register<IReadFromFileBytes>(factory =>\n            {\n                var mockedFileBytes = ProjectToSerialize.Data.GetFileBytes().ToArray();\n                var mockLinkedRomBytesProvider = TestUtil.CreateReadFromFileMock(mockedFileBytes);\n                return mockLinkedRomBytesProvider.Object;\n            });\n        }\n\n        public override IServiceContainer ConfigureAndRegisterServiceContainer()\n        {\n            var container = base.ConfigureAndRegisterServiceContainer();\n            \n            // normally we'd do all this in Configure().\n            // but if we do it here instead, we can override the regular system-level assignments.\n            \n            // overwrite the existing IProjectXmlSerializer instantiation registration so we can do some funny business. \n            container.Register<IProjectXmlSerializer>(factory =>\n            {\n                const int overriddenTargetSaveVersionForTests = 101;\n                \n                return new ProjectXmlSerializer(\n                    xmlSerializerFactory: factory.GetInstance<IXmlSerializerFactory>(),\n                    migrationRunner: factory.GetInstance<IMigrationRunner>(),\n                    \n                    // this creates a testing version of our migration runner that only tried to upgrade \n                    // from v100 to v101, and will not run anything else.\n                    migrateLoadedXmlToVersion: overriddenTargetSaveVersionForTests\n                    );\n            });\n            \n            return container;\n        }\n    }\n\n    // scenarios:\n    // A) we have a v100 save file, which means:\n    // - if the Cart Name in the xml matches the ROM, we're ok\n    // - if the Cart names don't match, but the checksums do, then it's likely we hit the v100 serializer bug.\n    //   in that case, the v100 mitigation should detect this and re-cache the cart name on load, upgrading it to v101.\n    //\n    // B) we have a > v100 save file which can ONLY mean:\n    // - if the Cart name OR the checksum in the XML don't match the rom, then we should reject the ROM.\n    public static TheoryData<Bug050Fixture> Fixtures =>\n        new()\n        {\n            // nothing wrong, no migrations needed, everything should work like normal.\n            new Bug050Fixture(forceOlderVersion100: true, overrideGameName: null, expectedCartTitleValidationException: false),\n            //\n            // older save file format, has a messed up cached name saved in project XML, our migrations should detect and run, fixing it.\n            // then when validation code runs, it shouldn't throw an exception\n            new Bug050Fixture(forceOlderVersion100: true, overrideGameName: \"BUGGY_NAME\", expectedCartTitleValidationException: false),\n            //\n            // newest save format. since there's no more buggy data caching, if the names don't match, then we KNOW\n            // there's a legit issue with the selected linked ROM file and we SHOULD rightly throw an exception \n            new Bug050Fixture(forceOlderVersion100: false, overrideGameName: \"LEGIT_PROB\", expectedCartTitleValidationException: true),\n        };\n\n    [Theory, MemberData(nameof(Fixtures))]\n    public void ExpectValidSerializationInput(Bug050Fixture fixture)\n    {\n        fixture.ProjectToSerialize.Should().NotBeNull();\n        fixture.ProjectToSerialize.Data.RomBytes[0].Rom.Should().Be(0x78);\n        fixture.FileIo.FakeFileBytes\n            .Should().NotBeNullOrEmpty(\"Expected some file bytes to be written by serialization process\");\n    }\n\n    [Theory, MemberData(nameof(Fixtures))]\n    public void ExpectedSetupOutputs(Bug050Fixture fixture)\n    {\n        if (fixture.ExpectedCartTitleValidationException)\n            return;\n        \n        fixture.DeserializedProject.Should().NotBeNull();\n        fixture.DeserializedProject!.Data.RomBytes[0].Rom.Should().Be(0x78);\n    }\n\n    [Theory, MemberData(nameof(Fixtures))]\n    public void ExpectSaveVersionsToMatch(Bug050Fixture fixture)\n    {\n        fixture.DeserializedSaveVersionBeforeMigrations.Should()\n            .Be(fixture.SaveVersionToUse, \"It was saved with the older file format\");\n    }\n\n    [Theory, MemberData(nameof(Fixtures))]\n    public void TestCartTitleOverridesApplied(Bug050Fixture fixture)\n    {\n        // if we're not using an override to intentionally create a buggy situation, no need to run anything else here\n        if (string.IsNullOrEmpty(fixture.BadGameName)) \n            return;\n        \n        Assert.NotEqual(\n            fixture.BeforeDeserializeInternalRomGameName,\n            fixture.BeforeSerializeCartTitleName);\n\n        // here's the bug: the deserialized data is wrong and needs to be fixed.\n        // this is invoked before the post-serialize migrations run, so will still be wrong.\n        // outside this callback, it should get fixed up automatically by the migration code.\n        if (!string.IsNullOrEmpty(fixture.BadGameName))\n            fixture.DeserializedInternalRomGameNameBeforeMigrations.Should().Be(\n                fixture.BadGameName,\n                \"Cart title used in test before migrations ran should result in using a bad game name here\");\n    }\n    \n    [Theory, MemberData(nameof(Fixtures))]\n    public void TestExceptionFiredWhenExpectedForNonMatchingData(Bug050Fixture fixture)\n    {\n        if (fixture.ExpectedCartTitleValidationException) \n            return;\n        \n        fixture.DeserializedProject.Should().NotBeNull(\"it should have deserialized correctly.\");\n        if (fixture.DeserializedProject == null)\n            return;\n\n        var deserializedGameName = fixture.DeserializedProject.InternalRomGameName;\n        deserializedGameName.Should()\n            .NotBe(fixture.BadGameName, \"Migrations should have fixed this\");\n\n        deserializedGameName.Should()\n            .Be(fixture.OriginalGoodGameName,\n                \"the automatic fix should have set this name back to the correct one\");\n    }\n}"
  },
  {
    "path": "Diz.Test/bugs/FileIoFixture.cs",
    "content": "﻿using System;\nusing Diz.Core.serialization;\nusing Moq;\n\nnamespace Diz.Test.bugs;\n\n/// <summary>\n/// Simulate reading and writing to the same file\n/// File bytes starts empty\n/// </summary>\npublic class FileIoFixture\n{\n    public byte[] FakeFileBytes { get; set; } = Array.Empty<byte>();\n    public Mock<IFileByteProvider> Mock { get; }\n\n    public FileIoFixture()\n    {\n        Mock = new Mock<IFileByteProvider>();\n        Mock.Setup(x => x.WriteBytes(It.IsAny<string>(), It.IsAny<byte[]>()))\n            .Callback((string filename, byte[] bytesToWrite) =>\n                FakeFileBytes = bytesToWrite\n            );\n\n        Mock.Setup(x => x.ReadAllBytes(It.IsAny<string>()))\n            .Returns<string>(_ =>\n                FakeFileBytes\n            );\n    }\n}"
  },
  {
    "path": "Diz.Test/external-tools/distribution info.txt",
    "content": "SuperFamicheck from https://github.com/Optiroc/SuperFamicheck/releases/tag/v1.0"
  },
  {
    "path": "Diz.Test/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net9.0\": {\n      \"BenchmarkDotNet\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.13.12, )\",\n        \"resolved\": \"0.13.12\",\n        \"contentHash\": \"aKnzpUZJJfLBHG7zcfQZhCexZQKcJgElC8qcFUTXPMYFlVauJBobuOmtRnmrapqC2j7EjjZCsPxa3yLvFLx5/Q==\",\n        \"dependencies\": {\n          \"BenchmarkDotNet.Annotations\": \"0.13.12\",\n          \"CommandLineParser\": \"2.9.1\",\n          \"Gee.External.Capstone\": \"2.3.0\",\n          \"Iced\": \"1.17.0\",\n          \"Microsoft.CodeAnalysis.CSharp\": \"4.1.0\",\n          \"Microsoft.Diagnostics.Runtime\": \"2.2.332302\",\n          \"Microsoft.Diagnostics.Tracing.TraceEvent\": \"3.0.2\",\n          \"Microsoft.DotNet.PlatformAbstractions\": \"3.1.6\",\n          \"Perfolizer\": \"[0.2.1]\",\n          \"System.Management\": \"5.0.0\"\n        }\n      },\n      \"ExtendedXmlSerializer\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.9.6, )\",\n        \"resolved\": \"3.9.6\",\n        \"contentHash\": \"kwnF5RjDwzle4cpUN4AWOCBAbntCkLQez5SIl2cY6hamWL33vEYu5Pp35BKkg/oklXnvaYobzJrFezRi9dl9tA==\",\n        \"dependencies\": {\n          \"LightInject\": \"6.6.1\",\n          \"NReco.LambdaParser\": \"1.0.12\",\n          \"Sprache\": \"2.3.1\",\n          \"System.Collections.Immutable\": \"7.0.0\"\n        }\n      },\n      \"FluentAssertions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.12.0, )\",\n        \"resolved\": \"6.12.0\",\n        \"contentHash\": \"ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==\",\n        \"dependencies\": {\n          \"System.Configuration.ConfigurationManager\": \"4.4.0\"\n        }\n      },\n      \"FluentAssertions.Analyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.29.1, )\",\n        \"resolved\": \"0.29.1\",\n        \"contentHash\": \"cHwAAoycOVv8/xS2NZhRJLOIbxzv+U5GeUm38nT1eGqvuNjoy7MKfGhR8aBFz0hkDlj7K9+gADd58fCZJYIQjQ==\"\n      },\n      \"FluentAssertions.ArgumentMatchers.Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.1.0, )\",\n        \"resolved\": \"3.1.0\",\n        \"contentHash\": \"NxeVGI7JlmGsyiqqJgNSH4RE2NUfFN3CwFwczirM8o5YixMwj0RuPkkXM+6RVAGD4RebSrO9vdgF0UungU7TeA==\",\n        \"dependencies\": {\n          \"FluentAssertions\": \"[6.0.0, 7.0.0)\",\n          \"Moq\": \"[4.13.0, 5.0.0)\"\n        }\n      },\n      \"JetBrains.Annotations\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2023.3.0, )\",\n        \"resolved\": \"2023.3.0\",\n        \"contentHash\": \"PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==\"\n      },\n      \"LightInject\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.6.4, )\",\n        \"resolved\": \"6.6.4\",\n        \"contentHash\": \"xId1F9OpzVvyNu3wcdPR4EcNUihcT5U9wiUJOi0tetuDOia1uAiFJzqmYGdRPsqAsgDb6o8sfoQCx5yMFFDsVw==\"\n      },\n      \"Microsoft.CodeAnalysis.Analyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.3.4, )\",\n        \"resolved\": \"3.3.4\",\n        \"contentHash\": \"AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==\"\n      },\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\",\n        \"dependencies\": {\n          \"Microsoft.CodeCoverage\": \"17.8.0\",\n          \"Microsoft.TestPlatform.TestHost\": \"17.8.0\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.20.70, )\",\n        \"resolved\": \"4.20.70\",\n        \"contentHash\": \"4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"5.1.1\"\n        }\n      },\n      \"System.Resources.Extensions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"psnQ6GRQOvt+evda5C4nD5EuV49mz2Tv0DD2JDVDEbE/TKoMukxSkGJcsBJ0pajpPuFRr67syFYlkJ4Wj6A5Zw==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.6.6, )\",\n        \"resolved\": \"2.6.6\",\n        \"contentHash\": \"MAbOOMtZIKyn2lrAmMlvhX0BhDOX/smyrTB+8WTXnSKkrmTGBS2fm8g1PZtHBPj91Dc5DJA7fY+/81TJ/yUFZw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"1.10.0\",\n          \"xunit.assert\": \"2.6.6\",\n          \"xunit.core\": \"[2.6.6]\"\n        }\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.3, )\",\n        \"resolved\": \"2.0.3\",\n        \"contentHash\": \"pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.10.0, )\",\n        \"resolved\": \"1.10.0\",\n        \"contentHash\": \"Lw8CiDy5NaAWcO6keqD7iZHYUTIuCOcoFrUHw5Sv84ITZ9gFeDybdkVdH0Y2maSlP9fUjtENyiykT44zwFQIHA==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.6.6, )\",\n        \"resolved\": \"2.6.6\",\n        \"contentHash\": \"74Cm9lAZOk5TKCz2MvCBCByKsS23yryOKDIMxH3XRDHXmfGM02jKZWzRA7g4mGB41GnBnv/pcWP3vUYkrCtEcg==\"\n      },\n      \"xunit.runner.console\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.6.6, )\",\n        \"resolved\": \"2.6.6\",\n        \"contentHash\": \"/tA9oQFdGhCWnS9VzBLboBeVNMANiT1O3tkK1CGvRwWIdx1Bwv/xN/pYcm6ZVB8Jvm8v5DyNywsqkc0VYBA+gw==\"\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.5.6, )\",\n        \"resolved\": \"2.5.6\",\n        \"contentHash\": \"CW6uhMXNaQQNMSG1IWhHkBT+V5eqHqn7MP0zfNMhU9wS/sgKX7FGL3rzoaUgt26wkY3bpf7pDVw3IjXhwfiP4w==\"\n      },\n      \"BenchmarkDotNet.Annotations\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.13.12\",\n        \"contentHash\": \"4zmFOOJqW1GrEP/t5XKgh97LH9r6zixGy2IA0JAaoTNNnZ8kPBt9u/XagsGNyV0e7rglOpFcWc6wI5EjefKpKA==\"\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.1.1\",\n        \"contentHash\": \"rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==\",\n        \"dependencies\": {\n          \"System.Diagnostics.EventLog\": \"6.0.0\"\n        }\n      },\n      \"CommandLineParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.9.1\",\n        \"contentHash\": \"OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==\"\n      },\n      \"CsvHelper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"30.0.3\",\n        \"contentHash\": \"OK4xEoPKFZYZ2Km+ZTGRxuLo33u+ipKVCC9ICCGn5JRiAmPLRsCPZ/shL3HKGPwS33MQSPcPHigI4HnX3Q47yg==\"\n      },\n      \"FluentValidation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.9.0\",\n        \"contentHash\": \"VneVlTvwYDkfHV5av3QrQ0amALgrLX6LV94wlYyEsh0B/klJBW7C8y2eAtj5tOZ3jH6CAVpr4s1ZGgew/QWyig==\"\n      },\n      \"Gee.External.Capstone\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.0\",\n        \"contentHash\": \"2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==\"\n      },\n      \"Iced\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.17.0\",\n        \"contentHash\": \"8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw==\"\n      },\n      \"JetBrains.FormatRipper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.1\",\n        \"contentHash\": \"9J+lWdztAczSqwgvOfMiTxEaaV6ONXY27Hq9VGbgzr9sUX8F5HCSvnd2/4J9xD1US4VL+ZPrREyRBl9NNbSGbw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"JetBrains.HabitatDetector\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"WQfQekJ2+BxM6+5anNhYWLyd6F4CRHW7pj8YoRXe6M5MDfPsZdYP0epx1+cHJkJuf0aeTB2JRK4KeEPW4UM8AQ==\",\n        \"dependencies\": {\n          \"JetBrains.FormatRipper\": \"2.0.1\"\n        }\n      },\n      \"JetBrains.Profiler.Api\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.0\",\n        \"contentHash\": \"r5xP8m9U6uHApcMyoL5yZL/Ltqz7auBo0MaboGdQoaawNpYnYr9PC3FFe+0mNT2pyOhuAfQC7WL/Gxb8M0v1Lw==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\"\n        }\n      },\n      \"JetBrains.Profiler.SelfApi\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"8dX8vSarO04JCm4n/RhDx2w/9mzKO+KI4rXtrt8AurGrKlhTgswuLZeGw5PuYsAUsJmmJrPBxJx4pTt/SW42aQ==\",\n        \"dependencies\": {\n          \"JetBrains.HabitatDetector\": \"1.0.2\",\n          \"JetBrains.Profiler.Api\": \"1.4.0\"\n        }\n      },\n      \"Microsoft.Bcl.AsyncInterfaces\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg==\"\n      },\n      \"Microsoft.CodeAnalysis.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"bNzTyxP3iD5FPFHfVDl15Y6/wSoI7e3MeV0lOaj9igbIKTjgrmuw6LoVJ06jUNFA7+KaDC/OIsStWl/FQJz6sQ==\",\n        \"dependencies\": {\n          \"Microsoft.CodeAnalysis.Analyzers\": \"3.3.3\",\n          \"System.Collections.Immutable\": \"5.0.0\",\n          \"System.Memory\": \"4.5.4\",\n          \"System.Reflection.Metadata\": \"5.0.0\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"5.0.0\",\n          \"System.Text.Encoding.CodePages\": \"4.5.1\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.4\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"sbu6kDGzo9bfQxuqWpeEE7I9P30bSuZEnpDz9/qz20OU6pm79Z63+/BsAzO2e/R/Q97kBrpj647wokZnEVr97w==\",\n        \"dependencies\": {\n          \"Microsoft.CodeAnalysis.Common\": \"[4.1.0]\"\n        }\n      },\n      \"Microsoft.CodeCoverage\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==\"\n      },\n      \"Microsoft.Diagnostics.NETCore.Client\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.2.251802\",\n        \"contentHash\": \"bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==\",\n        \"dependencies\": {\n          \"Microsoft.Bcl.AsyncInterfaces\": \"1.1.0\",\n          \"Microsoft.Extensions.Logging\": \"2.1.1\"\n        }\n      },\n      \"Microsoft.Diagnostics.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.2.332302\",\n        \"contentHash\": \"Hp84ivxSKIMTBzYSATxmUsm3YSXHWivcwiRRbsydGmqujMUK8BAueLN0ssAVEOkOBmh0vjUBhrq7YcroT7VCug==\",\n        \"dependencies\": {\n          \"Microsoft.Diagnostics.NETCore.Client\": \"0.2.251802\",\n          \"System.Collections.Immutable\": \"5.0.0\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"5.0.0\"\n        }\n      },\n      \"Microsoft.Diagnostics.Tracing.TraceEvent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.2\",\n        \"contentHash\": \"Pr7t+Z/qBe6DxCow4BmYmDycHe2MrGESaflWXRcSUI4XNGyznx1ttS+9JNOxLuBZSoBSPTKw9Dyheo01Yi6anQ==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.5.3\"\n        }\n      },\n      \"Microsoft.DotNet.PlatformAbstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.1.6\",\n        \"contentHash\": \"jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==\"\n      },\n      \"Microsoft.Extensions.Configuration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"LjVKO6P2y52c5ZhTLX/w8zc5H4Y3J/LJsgqTBj49TtFq/hAtVNue/WA0F6/7GMY90xhD7K0MDZ4qpOeWXbLvzg==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.1.1\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"VfuZJNa0WUshZ/+8BFZAhwFKiKuu/qOUCFntfdLpHj7vcRnsGHqd3G2Hse78DM+pgozczGM63lGPRLmy+uhUOA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Binder\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"fcLCTS03poWE4v9tSNBr3pWn0QwGgAn1vzqHXlXgvqZeOc7LvQNzaWcKRQZTdEc3+YhQKwMsOtm3VKSA2aWQ8w==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"2.1.1\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA==\"\n      },\n      \"Microsoft.Extensions.Logging\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"hh+mkOAQDTp6XH80xJt3+wwYVzkbwYQl9XZRCz4Um0JjP/o7N9vHM3rZ6wwwtr+BBe/L6iBO2sz0px6OWBzqZQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Binder\": \"2.1.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Options\": \"2.1.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"XRzK7ZF+O6FzdfWrlFTi1Rgj2080ZDsd46vzOjadHUB0Cz5kOvDG8vI7caa5YFrsHQpcfn0DxtjS4E46N4FZsA==\"\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.1.1\",\n          \"Microsoft.Extensions.Primitives\": \"2.1.1\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.1\",\n        \"contentHash\": \"scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg==\",\n        \"dependencies\": {\n          \"System.Memory\": \"4.5.1\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.5.1\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.3\",\n        \"contentHash\": \"3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==\"\n      },\n      \"Microsoft.TestPlatform.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==\",\n        \"dependencies\": {\n          \"NuGet.Frameworks\": \"6.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        }\n      },\n      \"Microsoft.TestPlatform.TestHost\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==\",\n        \"dependencies\": {\n          \"Microsoft.TestPlatform.ObjectModel\": \"17.8.0\",\n          \"Newtonsoft.Json\": \"13.0.1\"\n        }\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.Registry\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==\",\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"5.0.0\",\n          \"System.Security.Principal.Windows\": \"5.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"13.0.1\",\n        \"contentHash\": \"ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==\"\n      },\n      \"NReco.LambdaParser\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.12\",\n        \"contentHash\": \"f1nye8fMJCYfJeR+Qb7qlcvC8et4Y4TLf2GLfaFoeISgI+yisaJkbNSGfKCpFBNeVF/iPDkyFmr4J28dv342/Q==\"\n      },\n      \"NuGet.Frameworks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.5.0\",\n        \"contentHash\": \"QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==\"\n      },\n      \"Perfolizer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.2.1\",\n        \"contentHash\": \"Dt4aCxCT8NPtWBKA8k+FsN/RezOQ2C6omNGm5o/qmYRiIwlQYF93UgFmeF1ezVNsztTnkg7P5P63AE+uNkLfrw==\",\n        \"dependencies\": {\n          \"System.Memory\": \"4.5.3\"\n        }\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"SharpZipLib\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.4.2\",\n        \"contentHash\": \"yjj+3zgz8zgXpiiC3ZdF/iyTBbz2fFvMxZFEBPUcwZjIvXOf37Ylm+K58hqMfIBt5JgU/Z2uoUS67JmTLe973A==\"\n      },\n      \"Sprache\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.3.1\",\n        \"contentHash\": \"Q+mXeiTxiUYG3lKYF6TS82/SyB4F2613Q1yXTMwg4jWGHEEVC3yrzHtNcI4B3qnDI0+eJsezGJ0V+cToUytHWw==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Private.Uri\": \"4.3.2\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\"\n        }\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.CodeDom\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==\"\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Immutable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.0.0\",\n        \"contentHash\": \"dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==\"\n      },\n      \"System.Configuration.ConfigurationManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==\",\n        \"dependencies\": {\n          \"System.Security.Cryptography.ProtectedData\": \"4.4.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.EventLog\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==\"\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Management\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"5.0.0\",\n          \"Microsoft.Win32.Registry\": \"5.0.0\",\n          \"System.CodeDom\": \"5.0.0\"\n        }\n      },\n      \"System.Memory\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.4\",\n        \"contentHash\": \"1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==\"\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Private.Uri\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.2\",\n        \"contentHash\": \"o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.1\",\n          \"Microsoft.NETCore.Targets\": \"1.1.3\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Metadata\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==\"\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.AccessControl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"5.0.0\",\n          \"System.Security.Principal.Windows\": \"5.0.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.ProtectedData\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==\"\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.0\",\n        \"contentHash\": \"t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==\"\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==\"\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.4\",\n        \"contentHash\": \"zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==\"\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.6.6\",\n        \"contentHash\": \"tqi7RfaNBqM7t8zx6QHryuBPzmotsZXKGaWnopQG2Ez5UV7JoWuyoNdT6gLpDIcKdGYey6YTXJdSr9IXDMKwjg==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.6.6]\",\n          \"xunit.extensibility.execution\": \"[2.6.6]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.6.6\",\n        \"contentHash\": \"ty6VKByzbx4Toj4/VGJLEnlmOawqZiMv0in/tLju+ftA+lbWuAWDERM+E52Jfhj4ZYHrAYVa14KHK5T+dq0XxA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.3\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.6.6\",\n        \"contentHash\": \"UDjIVGj2TepVKN3n32/qXIdb3U6STwTb9L6YEwoQO2A8OxiJS5QAVv2l1aT6tDwwv/9WBmm8Khh/LyHALipcng==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.6.6]\"\n        }\n      },\n      \"diz.controllers\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Import\": \"[1.0.0, )\",\n          \"Diz.LogWriter\": \"[1.0.0, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      },\n      \"diz.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"FluentValidation\": \"[11.9.0, )\",\n          \"JetBrains.Annotations\": \"[2023.3.0, )\",\n          \"JetBrains.Profiler.SelfApi\": \"[2.5.0, )\",\n          \"LightInject\": \"[6.6.4, )\",\n          \"SharpZipLib\": \"[1.4.2, )\",\n          \"System.Diagnostics.Tracing\": \"[4.3.0, )\",\n          \"System.Resources.Extensions\": \"[8.0.0, )\",\n          \"System.Text.Encoding.CodePages\": \"[8.0.0, )\"\n        }\n      },\n      \"diz.core.interfaces\": {\n        \"type\": \"Project\"\n      },\n      \"diz.cpu.65816\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Core.Interfaces\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.import\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\"\n        }\n      },\n      \"diz.logwriter\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CsvHelper\": \"[30.0.3, )\",\n          \"Diz.Core\": \"[1.0.0, )\",\n          \"Diz.Cpu.65816\": \"[1.0.0, )\",\n          \"ExtendedXmlSerializer\": \"[3.9.6, )\",\n          \"LightInject\": \"[6.6.4, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "Diz.Ui.Eto/Diz.Ui.Eto.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0-windows</TargetFramework>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n    </PropertyGroup>\n\n    <ItemGroup>\n      <PackageReference Include=\"Eto.Forms\">\n        <Version>2.9.0</Version>\n      </PackageReference>\n      <PackageReference Include=\"Eto.Platform.Windows\">\n        <Version>2.9.0</Version>\n      </PackageReference>\n      <PackageReference Include=\"JetBrains.Annotations\">\n        <Version>2023.3.0</Version>\n      </PackageReference>\n      <PackageReference Include=\"LightInject\">\n        <Version>6.6.4</Version>\n      </PackageReference>\n    </ItemGroup>\n\n    <ItemGroup>\n      <ProjectReference Include=\"..\\Diz.Controllers\\Diz.Controllers\\Diz.Controllers.csproj\" />\n    </ItemGroup>\n\n    <ItemGroup>\n      <None Remove=\"images\\diz-icon2-magenta-med32x32.ico\" />\n      <EmbeddedResource Include=\"resource\\diz-icon2-magenta-med32x32.ico\" />\n    </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Ui.Eto/DizEtoAppSettingsProvider.cs",
    "content": "﻿using System.ComponentModel;\nusing Diz.Controllers.interfaces;\n\nnamespace Diz.Ui.Eto;\n\n// TODO: implement property changed notification. also,\n// TODO: combine this with the other classes that do the same thing, don't rely on\n//       winforms/etc for this\n// ReSharper disable once ClassNeverInstantiated.Global\npublic class DizEtoAppSettingsProvider : IDizAppSettings\n{\n    public event PropertyChangedEventHandler? PropertyChanged;\n    public string LastProjectFilename { get; set; }\n    public bool OpenLastFileAutomatically { get; set; }\n    public string LastOpenedFile { get; set; }\n}"
  },
  {
    "path": "Diz.Ui.Eto/DizUiEtoCompositionRoot.cs",
    "content": "using Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Ui.Eto.ui;\nusing JetBrains.Annotations;\nusing LightInject;\n\nnamespace Diz.Ui.Eto;\n\n[UsedImplicitly] public class DizUiEtoCompositionRoot : ICompositionRoot\n{\n    public void Compose(IServiceRegistry serviceRegistry)\n    {\n        // TODO: register Eto-specific versions of this stuff\n        serviceRegistry.Register<IMainGridWindowView, EtoMainGridForm>(\"MainGridWindowView\");\n        serviceRegistry.Register<IProgressView, EtoProgressForm>(\"ProgressBarView\");\n        // serviceRegistry.Register<IFormViewer, PlaceholderForm>(\"AboutView\");\n        // serviceRegistry.Register<IImportRomDialogView, PlaceholderForm>(\"ImportRomView\");\n        // serviceRegistry.Register<ILogCreatorSettingsEditorView, PlaceholderForm>(\"ExportDisassemblyView\");\n        // serviceRegistry.Register<ILabelEditorView, PlaceholderForm>(\"LabelEditorView\");\n        \n        serviceRegistry.RegisterSingleton<IDizAppSettings, DizEtoAppSettingsProvider>();\n    }\n}"
  },
  {
    "path": "Diz.Ui.Eto/EtoCommonGui.cs",
    "content": "﻿using Diz.Controllers.interfaces;\nusing Eto.Forms;\n\nnamespace Diz.Ui.Eto;\n\npublic class EtoCommonGui : ICommonGui\n{\n    public bool PromptToConfirmAction(string msg) => \n        MessageBox.Show(msg, \"Confirm\", MessageBoxButtons.OKCancel, MessageBoxType.Question) == DialogResult.Ok;\n\n    public void ShowError(string msg) => \n        MessageBox.Show(msg, \"Error\", MessageBoxButtons.OK, MessageBoxType.Error);\n\n    public void ShowWarning(string msg) => \n        MessageBox.Show(msg, \"Warning\", MessageBoxButtons.OK, MessageBoxType.Warning);\n\n    public void ShowMessage(string msg) => \n        MessageBox.Show(msg, \"Info\", MessageBoxButtons.OK);\n}"
  },
  {
    "path": "Diz.Ui.Eto/Properties/AssemblyInfo.cs",
    "content": "﻿using Diz.Ui.Eto;\nusing LightInject;\n\n[assembly: CompositionRootType(typeof(DizUiEtoCompositionRoot))]"
  },
  {
    "path": "Diz.Ui.Eto/ui/EtoMainGridForm.cs",
    "content": "﻿using System.ComponentModel;\nusing Diz.Controllers.controllers;\nusing Diz.Controllers.interfaces;\nusing Diz.Core.model;\nusing Diz.LogWriter;\nusing Eto.Drawing;\nusing Eto.Forms;\nusing Label = Eto.Forms.Label;\n\nnamespace Diz.Ui.Eto.ui;\n\npublic class SampleMenuItem : Command\n{\n    public SampleMenuItem()\n    {\n        MenuText = \"C&lick Me, Command\";\n        ToolBarText = \"Click Me\";\n        ToolTip = \"This shows a dialog for no reason\";\n        // Image = Bitmap.FromResource (\"MyResourceName.png\");\n        Shortcut = Application.Instance.CommonModifier | Keys.M;  // control+M or cmd+M\n    }\n\n    protected override void OnExecuted(EventArgs e)\n    {\n        base.OnExecuted(e);\n        MessageBox.Show(Application.Instance.MainForm, \"You clicked me!\", \"Tutorial 2\", MessageBoxButtons.OK);\n    }\n}\n\npublic class EtoMainGridForm : Form, IMainGridWindowView\n{\n    public event EventHandler? OnFormClosed;\n\n    private readonly IDizDocument document;\n    private readonly IDizAppSettings appSettings;\n    private readonly IViewFactory viewFactory;\n    public IProjectController ProjectController { get; }\n\n    public EtoMainGridForm(\n        IProjectController projectController,\n        IDizAppSettings appSettings,\n        IDizDocument document,\n        IViewFactory viewFactory)\n    {\n        CreateGui();\n\n        this.document = document;\n        this.appSettings = appSettings;\n        this.viewFactory = viewFactory;\n        this.ProjectController = projectController;\n        this.ProjectController.ProjectView = this;\n\n        // TODO\n        // aliasList = viewFactory.GetLabelEditorView();\n        // aliasList.ProjectController = this.projectController;\n\n        this.document.PropertyChanged += Document_PropertyChanged;\n        this.ProjectController.ProjectChanged += ProjectController_ProjectChanged;\n\n        // NavigationForm = new NavigationForm // TODO\n        // {\n        //     Document = this.document,\n        //     SnesNavigation = this,\n        // };\n\n        Closed += (sender, args) => OnFormClosed?.Invoke(sender, args);\n    }\n\n    private void ProjectController_ProjectChanged(object sender, IProjectController.ProjectChangedEventArgs e)\n    {\n    }\n\n    private void Document_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n    {\n    }\n\n    private void CreateMenu()\n    {\n        Menu = new MenuBar\n        {\n            Items =\n            {\n                new ButtonMenuItem\n                {\n                    Text = \"&File\",\n                    Items =\n                    {\n                        // you can add commands or menu items\n                        new SampleMenuItem(),\n                        // another menu item, not based off a Command\n                        new ButtonMenuItem { Text = \"Click Me, MenuItem\" }\n                    }\n                }\n            }\n        };\n    }\n    \n    private GridView gridView;\n    \n    private void CreateGui()\n    {\n        Title = \"Diz\";\n        Icon = Icon.FromResource(\"Diz.Ui.Eto.resource.diz-icon2-magenta-med32x32.ico\");\n        CreateMenu();\n\n        // Create the grid view\n        gridView = new GridView\n        {\n            AllowMultipleSelection = false,\n            ShowHeader = true,\n        };\n\n        // Add columns to the grid\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"Label\",\n            Width = 200,\n            DataCell = new TextBoxCell(\"Label\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"PC\",\n            Width = 58,\n            DataCell = new TextBoxCell(\"PC\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"@\",\n            Width = 26,\n            DataCell = new TextBoxCell(\"Char\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"#\",\n            Width = 26,\n            DataCell = new TextBoxCell(\"Hex\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"<*>\",\n            Width = 34,\n            DataCell = new TextBoxCell(\"Points\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"Instruction\",\n            Width = 125,\n            DataCell = new TextBoxCell(\"Instruction\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"IA\",\n            Width = 58,\n            DataCell = new TextBoxCell(\"IA\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"Flag\",\n            Width = 86,\n            DataCell = new TextBoxCell(\"Flag\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"B\",\n            Width = 26,\n            DataCell = new TextBoxCell(\"DB\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"D\",\n            Width = 42,\n            DataCell = new TextBoxCell(\"DP\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"M\",\n            Width = 26,\n            DataCell = new TextBoxCell(\"M\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"X\",\n            Width = 26,\n            DataCell = new TextBoxCell(\"X\")\n        });\n\n        gridView.Columns.Add(new GridColumn\n        {\n            HeaderText = \"Comment\",\n            Expand = true, // Auto-size remaining space\n            DataCell = new TextBoxCell(\"Comment\")\n        });\n        \n        // needed to set the initial size of the grid or things seem to get weird.\n        gridView.Size = new Size(1000, 600);\n\n        var bottomFooter = new Label\n        {\n            Text = \"[% Complete etc TODO]\",\n            Size = new Size { Height = 20 }\n        };\n        \n        var layout = new DynamicLayout();\n        layout.BeginVertical(yscale: true);\n        layout.AddRow (gridView);\n        layout.EndVertical ();\n        \n        layout.BeginVertical ();\n        layout.AddRow (bottomFooter);\n        layout.EndVertical ();\n        \n        // Apply the container as the content of the form\n        Content = layout;\n        \n        // hacky nonsense: need to show the form, set the data, then invalidate, or we get weird rendering bugs in\n        // Winforms with black unpainted grid cells etc\n        Show();\n        gridView.DataStore = GetGridData();\n        Invalidate(true);\n    }\n\n    private IEnumerable<object> GetGridData()\n    {\n        return new List<object>\n        {\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n            new { Label = \"Item1\", PC = \"0x001\", Char = \"@\", Hex = \"0xFF\", Points = \"10\", Instruction = \"NOP\", IA = \"Yes\", Flag = \"OK\", DB = \"12\", DP = \"34\", M = \"Yes\", X = \"No\", Comment = \"Test Row 1\" },\n            new { Label = \"Item2\", PC = \"0x002\", Char = \"&\", Hex = \"0xFA\", Points = \"15\", Instruction = \"ADD\", IA = \"No\", Flag = \"WARN\", DB = \"34\", DP = \"12\", M = \"No\", X = \"Yes\", Comment = \"Test Row 2\" },\n        };\n    }\n\n\n    public ILongRunningTaskHandler.LongRunningTaskHandler TaskHandler =>\n        ProgressBarJob.RunAndWaitForCompletion;\n\n    public void SelectOffset(int pcOffset, ISnesNavigation.HistoryArgs? historyArgs = null)\n    {\n    }\n\n    public void SelectOffsetWithOvershoot(int pcOffset, int overshootAmount = 0)\n    {\n    }\n\n    public int SelectedOffset => -1; // temp, fixme.\n\n    public Project Project { get; set; }\n\n    public void OnProjectOpenFail(string errorMsg)\n    {\n    }\n\n    public void OnProjectSaved()\n    {\n    }\n\n    public void OnExportFinished(LogCreatorOutput.OutputResult result)\n    {\n    }\n\n    public string AskToSelectNewRomFilename(string promptSubject, string promptText)\n    {\n        return \"\";\n    }\n\n    public void OnProjectOpenWarnings(IEnumerable<string> warnings)\n    {\n    }\n\n    public void BringFormToTop() => Focus();\n}"
  },
  {
    "path": "Diz.Ui.Eto/ui/EtoProgressForm.cs",
    "content": "﻿using Diz.Controllers.interfaces;\nusing Eto.Drawing;\nusing Eto.Forms;\n\nnamespace Diz.Ui.Eto.ui;\n\npublic class EtoProgressForm : Dialog, IProgressView\n{\n    public event EventHandler? OnFormClosed;\n    private Label mainText;\n\n    public EtoProgressForm()\n    {\n        InitializeComponent();\n    }\n\n    private void InitializeComponent()\n    {\n        Title = \"Progress Update\";\n        ClientSize = new Size(400, 600);\n        mainText = new Label { Text = \"Please Wait\" };\n        Content = mainText;\n    }\n\n    public void Show()\n    {\n        // todo? needed?\n    }\n\n    public void BringFormToTop()\n    {\n        Focus();\n    }\n\n    public bool PromptDialog()\n    {\n        Show(); // unsure of equivalent in Eto.\n        // probably should make this class derive from Dialog<bool> instead and rework stuff for that.\n        return true;\n    }\n\n    public void Report(int value)\n    {\n        // TODO: update text\n        mainText.Text = $\"Progress: {value}%\";\n    }\n\n    public bool IsMarquee { get; set; }\n    public required string TextOverride { get; set; }\n    public bool IsVisible() => \n        Application.Instance.Invoke(() => Visible);\n\n    public void SignalJobIsDone() => \n        Application.Instance.Invoke(Close);\n}"
  },
  {
    "path": "Diz.Ui.Eto/ui/PlaceholderForm.cs",
    "content": "﻿using Eto.Drawing;\nusing Eto.Forms;\n\nnamespace Diz.Ui.Eto.ui;\n\npublic class PlaceholderForm : Form\n{\n    public PlaceholderForm ()\n    {\n        Title = \"Diz\";\n        ClientSize = new Size(800, 600);\n        Content = new Label { Text = \"Placeholder form - TODO: IMPLEMENT ME\" };\n    }\n}"
  },
  {
    "path": "Diz.Ui.Views/Diz.Ui.Views.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net9.0</TargetFramework>\n        <ImplicitUsings>enable</ImplicitUsings>\n        <Nullable>enable</Nullable>\n        <RootNamespace>Diz.Views</RootNamespace>\n    </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "Diz.Ui.Views/ProjectViewer.cs",
    "content": "﻿namespace Diz.Views;\n\npublic class ProjectViewer\n{\n}"
  },
  {
    "path": "DiztinGUIsh.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.30503.244\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.App.Winforms\", \"Diz.App.Winforms\\Diz.App.Winforms.csproj\", \"{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Core\", \"Diz.Core\\Diz.Core.csproj\", \"{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Test\", \"Diz.Test\\Diz.Test.csproj\", \"{594DE30A-343F-4C09-828D-65A67626A491}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.App.PowerShell\", \"Diz.App.PowerShell\\Diz.App.PowerShell.csproj\", \"{90309EF0-855F-4B9C-B501-53C5BF1E72BD}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.LogWriter\", \"Diz.LogWriter\\Diz.LogWriter.csproj\", \"{96859F8F-5921-409C-A7A2-ECECDE5F70A7}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Controllers\", \"Diz.Controllers\\Diz.Controllers\\Diz.Controllers.csproj\", \"{B2213BCE-F70F-423F-BB55-0744243FAD43}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Core.Interfaces\", \"Diz.Core.Interfaces\\Diz.Core.Interfaces.csproj\", \"{E5AA827C-2194-4126-A1C2-0A5F9B9F653C}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Ui.Winforms\", \"Diz.Ui.Winforms\\Diz.Ui.Winforms\\Diz.Ui.Winforms.csproj\", \"{CCB93C20-0A89-473A-888A-B8C8D23276E7}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Import\", \"Diz.Import\\Diz.Import.csproj\", \"{A2303006-DBCD-4C5C-8360-C3EBC085C493}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Cpu.65816\", \"Diz.Cpu.65816\\Diz.Cpu.65816.csproj\", \"{80D19FA4-BD5B-4189-8F0B-8A634A6738DD}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Ui.Winforms.Test\", \"Diz.Ui.Winforms\\Diz.Ui.Winforms.Test\\Diz.Ui.Winforms.Test.csproj\", \"{1D02D3F9-DA94-4930-9D2A-8B59CABB13A0}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{594DE30A-343F-4C09-828D-65A67626A491} = {594DE30A-343F-4C09-828D-65A67626A491}\n\t\t{CCB93C20-0A89-473A-888A-B8C8D23276E7} = {CCB93C20-0A89-473A-888A-B8C8D23276E7}\n\t\t{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3} = {2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Controllers.Test\", \"Diz.Controllers\\Diz.Controllers.Test\\Diz.Controllers.Test\\Diz.Controllers.Test.csproj\", \"{B2B93DFB-BF78-419B-ADD0-9308973ECAC1}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Ui.Eto\", \"Diz.Ui.Eto\\Diz.Ui.Eto.csproj\", \"{31B6052F-721D-4CBA-87D5-B289B9389D3D}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.Ui.Views\", \"Diz.Ui.Views\\Diz.Ui.Views.csproj\", \"{42F88470-39F9-493A-95D4-4D90ED486374}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"App\", \"App\", \"{4CA2997D-F2EF-4213-A3A8-4FF2993BB6CB}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Core\", \"Core\", \"{3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"UI\", \"UI\", \"{5014F089-4989-4E78-B14F-44C83F54DFC4}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.App.Common\", \"Diz.App.Common\\Diz.App.Common.csproj\", \"{945A466E-F201-463F-BD38-58F512DD646D}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Diz.App.Eto\", \"Diz.App.Eto\\Diz.App.Eto.csproj\", \"{0C6E2C5E-989B-47A2-9121-30E207403045}\"\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{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{594DE30A-343F-4C09-828D-65A67626A491}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{594DE30A-343F-4C09-828D-65A67626A491}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{594DE30A-343F-4C09-828D-65A67626A491}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{594DE30A-343F-4C09-828D-65A67626A491}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{90309EF0-855F-4B9C-B501-53C5BF1E72BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{90309EF0-855F-4B9C-B501-53C5BF1E72BD}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{90309EF0-855F-4B9C-B501-53C5BF1E72BD}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{90309EF0-855F-4B9C-B501-53C5BF1E72BD}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{96859F8F-5921-409C-A7A2-ECECDE5F70A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{96859F8F-5921-409C-A7A2-ECECDE5F70A7}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{96859F8F-5921-409C-A7A2-ECECDE5F70A7}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{96859F8F-5921-409C-A7A2-ECECDE5F70A7}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B2213BCE-F70F-423F-BB55-0744243FAD43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B2213BCE-F70F-423F-BB55-0744243FAD43}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B2213BCE-F70F-423F-BB55-0744243FAD43}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B2213BCE-F70F-423F-BB55-0744243FAD43}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E5AA827C-2194-4126-A1C2-0A5F9B9F653C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E5AA827C-2194-4126-A1C2-0A5F9B9F653C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E5AA827C-2194-4126-A1C2-0A5F9B9F653C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E5AA827C-2194-4126-A1C2-0A5F9B9F653C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{CCB93C20-0A89-473A-888A-B8C8D23276E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CCB93C20-0A89-473A-888A-B8C8D23276E7}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CCB93C20-0A89-473A-888A-B8C8D23276E7}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CCB93C20-0A89-473A-888A-B8C8D23276E7}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{A2303006-DBCD-4C5C-8360-C3EBC085C493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A2303006-DBCD-4C5C-8360-C3EBC085C493}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A2303006-DBCD-4C5C-8360-C3EBC085C493}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A2303006-DBCD-4C5C-8360-C3EBC085C493}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{80D19FA4-BD5B-4189-8F0B-8A634A6738DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{80D19FA4-BD5B-4189-8F0B-8A634A6738DD}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{80D19FA4-BD5B-4189-8F0B-8A634A6738DD}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{80D19FA4-BD5B-4189-8F0B-8A634A6738DD}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{1D02D3F9-DA94-4930-9D2A-8B59CABB13A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{1D02D3F9-DA94-4930-9D2A-8B59CABB13A0}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{1D02D3F9-DA94-4930-9D2A-8B59CABB13A0}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{1D02D3F9-DA94-4930-9D2A-8B59CABB13A0}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B2B93DFB-BF78-419B-ADD0-9308973ECAC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B2B93DFB-BF78-419B-ADD0-9308973ECAC1}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B2B93DFB-BF78-419B-ADD0-9308973ECAC1}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B2B93DFB-BF78-419B-ADD0-9308973ECAC1}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{31B6052F-721D-4CBA-87D5-B289B9389D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{31B6052F-721D-4CBA-87D5-B289B9389D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{31B6052F-721D-4CBA-87D5-B289B9389D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{31B6052F-721D-4CBA-87D5-B289B9389D3D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{42F88470-39F9-493A-95D4-4D90ED486374}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{42F88470-39F9-493A-95D4-4D90ED486374}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{42F88470-39F9-493A-95D4-4D90ED486374}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{42F88470-39F9-493A-95D4-4D90ED486374}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{945A466E-F201-463F-BD38-58F512DD646D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{945A466E-F201-463F-BD38-58F512DD646D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{945A466E-F201-463F-BD38-58F512DD646D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{945A466E-F201-463F-BD38-58F512DD646D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{0C6E2C5E-989B-47A2-9121-30E207403045}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{0C6E2C5E-989B-47A2-9121-30E207403045}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{0C6E2C5E-989B-47A2-9121-30E207403045}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{0C6E2C5E-989B-47A2-9121-30E207403045}.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 = {231EF970-F537-490F-B331-FB074A426C3A}\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{90309EF0-855F-4B9C-B501-53C5BF1E72BD} = {4CA2997D-F2EF-4213-A3A8-4FF2993BB6CB}\n\t\t{2A2DD3C1-9E64-4CD7-98A5-310D9FED2CA3} = {4CA2997D-F2EF-4213-A3A8-4FF2993BB6CB}\n\t\t{B2213BCE-F70F-423F-BB55-0744243FAD43} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{50646B00-03D8-4CEC-9ED1-3EF4CB199E8B} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{B2B93DFB-BF78-419B-ADD0-9308973ECAC1} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{E5AA827C-2194-4126-A1C2-0A5F9B9F653C} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{80D19FA4-BD5B-4189-8F0B-8A634A6738DD} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{A2303006-DBCD-4C5C-8360-C3EBC085C493} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{96859F8F-5921-409C-A7A2-ECECDE5F70A7} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{594DE30A-343F-4C09-828D-65A67626A491} = {3ABC99AB-7605-4C4C-8EC6-A7C64E86C1E5}\n\t\t{CCB93C20-0A89-473A-888A-B8C8D23276E7} = {5014F089-4989-4E78-B14F-44C83F54DFC4}\n\t\t{1D02D3F9-DA94-4930-9D2A-8B59CABB13A0} = {5014F089-4989-4E78-B14F-44C83F54DFC4}\n\t\t{31B6052F-721D-4CBA-87D5-B289B9389D3D} = {5014F089-4989-4E78-B14F-44C83F54DFC4}\n\t\t{42F88470-39F9-493A-95D4-4D90ED486374} = {5014F089-4989-4E78-B14F-44C83F54DFC4}\n\t\t{945A466E-F201-463F-BD38-58F512DD646D} = {4CA2997D-F2EF-4213-A3A8-4FF2993BB6CB}\n\t\t{0C6E2C5E-989B-47A2-9121-30E207403045} = {4CA2997D-F2EF-4213-A3A8-4FF2993BB6CB}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "DiztinGUIsh.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=DiztinGUIsh_002EAnnotations/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "LICENSE.md",
    "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>."
  },
  {
    "path": "README.md",
    "content": "# DiztinGUIsh (\"Diz\")\n[![Build Status](https://github.com/Dotsarecool/DiztinGUIsh/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Dotsarecool/DiztinGUIsh/actions/workflows/dotnet.yml)\n\nA Super NES ROM Disassembler and tracelog capture/analysis tool with a focus on collaborative workflow UX. Exports .asm files ready to be compiled back into the original binary. Written in Winforms/C#.\n\n_Diz tools suite:_\n![image](https://user-images.githubusercontent.com/5413064/110195709-45767d80-7e0d-11eb-9f5f-1e21489dc8cd.png)\n\nOfficial support channel is #diztinguish in the https://sneslab.net/ discord\n\n---\n\n# Features\n\n## Main features\n\n**Disassembling programs** (like SNES games) for some CPU architectures (like the SNES's 658016) is a pain because you have to know a lot of information about the program at the point where it's running. Diz is designed to make this less of a nightmare.\n\n_Demo of basic disassembling:_\n![ezgif com-gif-maker](https://i.imgur.com/Tb2H484.gif)\n\nView more docs here: https://github.com/IsoFrieze/DiztinGUIsh/blob/master/Diz.App.Winforms/dist/docs/HELP.md\n\n---\n\n**Realtime tracelog capturing**: We provide a tight integration with a custom BSNES build to capture CPU tracelog data over a socket connection. You don't have to play the game at 2FPS anymore, or deal with wrangling gigabyte-sized tracelog files.  Simply hit 'capture' and Diz will talk directly to a running BSNES CPU, capturing data for as long as you like. Turn the ROM visualizer on and watch this process in realtime.\n\n![ezgif com-gif-maker](https://user-images.githubusercontent.com/5413064/97286056-69033900-1819-11eb-925d-67e1bbce95a7.gif)\n![image](https://user-images.githubusercontent.com/5413064/97133932-ed729080-1721-11eb-894e-4c110787aa75.png)\n\nFor more details, visit the [Tracelog capturing tutorial](https://github.com/Dotsarecool/DiztinGUIsh/blob/master/TRACE%20CAPTURE%20INSTRUCTIONS.md)\n\n## Other useful features\n\n- Tracelog file import support for Bizhawk and BSNES (record where the CPU is executing and what flags are set)\n- BSNES usage map import / Bizhawk CDL import (record which sections of ROM are code vs data)\n- Annotation of ROM and RAM addresses, labels, and comments. These are exported in the assembly output for humans\n- Merge-friendly XML based file format. Save your project file with a .dizraw extension (~1.5MB), and the uncompressed XML is easy to share, collaborate, and merge with other people easily.  Great for group aggregration projects or building a database from various sources of info laying around the internet. Re-export the assembly and generate code with everyone's collective efforts stored in one place. Say goodbye to search+replace for adding labels and variable names all over the place.\n- ROM visualizer, view which parts of the ROM you've marked as code vs data, and see visual progress.\n- C# .NET WinForms app, easy to add features to. Write your own plugins or use our plumbing or GUI as a base for your own tools.\n\nNOTE: Works fine with stock asar though, there's a bugfix you may want:\n- https://github.com/binary1230/asar/tree/fix_relative_addressing/src/asar\n\n## Details\n\n### Doesn't this already exist?\n\nThere is at least one 65C816 disassembler out there already. The biggest issue with it (not with that program, but with disassembling 65C816 in general) is that some instructions assemble to different sizes depending on context. This makes it difficult to automate. \n\nA ROM contains two broad categories of stuff in it: code and data. A perfect disassembler would isolate the code, disassemble it, and leave the data as it is (or maybe neatly format it). Differentiating data from code is already kinda hard, especially if the size of the data isn't explicitly stated. A perfect program would need context to do its job. Turns out that keeping track of all memory and providing context for these situations is pretty much emulation. Some emulators have code/data loggers (CDLs) that mark every executed byte as an instruction and every read byte as data for the purpose of disassembly. A naive approach to disassembling then, would be to disassemble *everything* as code, then leave it up to a person to go back and mark the data manually. Disassembling code is the most tedius part, so this isn't a bad approach.\n\nIn the 65C816 instruction set, several instructions assemble to different lengths depending on whether or not a bit is currently set or reset in the processor flag P register. For example, the sequence `C9 00 F0 48` could be `CMP.W #$F000 : PHA` or `CMP.B #$00 : BEQ +72` depending on if the accumulator size flag M is 0 or 1. You could guess, but if you're wrong, the next however many instructions may be incorrect due to treating operands (`#$F0`) as opcodes (`BEQ`). This is known as *desynching*. So now you need context just to be able to disassemble code too.\n\nNow for the most part, you can get away with just disassembling instructions as you hit them, following jumps and branches, and only keeping track of the M and X flags to make sure the special instructions are disassmbled properly. But more likely than not there will be some jump instructions that depend on values in RAM. Keeping track of all RAM just to get those effective addresses would be silly--again, it would basically be emulation at that point. You'll need to manually determine the set of jumps possible, and start new disassmble points from each of those values. Don't forget to carry over those M and X flags!\n\nThings get more complicated if you want to determine the effective address of an instruction. Instructions like `LDA.L $038CDA,X` have the effective address right in the instruction (`$038CDA`). But most instructions look something like `STA.B $03`. The full effective address needs to be deduced from the data bank and direct page registers. Better keep track of those too!\n\nSo to take all of this into consideration, DiztinGUIsh tries to make the manual parts of disassembling accurately as speedy as possible, while still automating the easy parts. The goal of this software is to produce an accurate, clean disassembly of SNES games in a timely manner. Any time things look dicey, or if it becomes impossible to disassemble something accurately, the program will pause and wait for more input. Of course, there are options to go ham and just ignore warnings, but proceed at your own risk!\n\n## Features\n\nImplemented or currently in progress:\n\n* Manual and Auto Stepping\n* Stepping into a branch or call\n* Goto effective address\n* Goto first or nearby unreached data\n* Marking data types (plain data, graphics, pointers, etc.)\n* Tracking M & X flags, Data Bank & Direct Page registers\n* Producing a customizable output file that assembles with asar\n\nPlanned stuff:\n\n* SPC700 & SuperFX architechtures\n* Merging multiple project files together\n* Better labelling effective addresses that aren't ROM\n* Programmable data viewer to locate graphics easily\n* Setting a \"base\" per instruction for relocateable code\n* Option to put large data blocks into separate .bin files intead of in the .asm\n* Scripting engine & API\n\n\n### \"Distinguish\" but with a 'z' because it's rad. It's also a GUI application so might as well highlight that fact.\"\n\n\n### Other tips \n\n- On Win11, if you have DPI or screen issues (fonts messed up or bunched up or too small):\nRight click on Diztinguish.exe, Compatibility, change high dpi settings, Override high DPI scaling behavior and select Scaling performed by System."
  },
  {
    "path": "TRACE CAPTURE INSTRUCTIONS.md",
    "content": "# HOWTO: Live tracelog capture with Diz+BSNES+\n\n## Install Main Tools\n1. Diz: https://github.com/Dotsarecool/DiztinGUIsh/releases/ (use latest release. must be >= 2.2.1.7)\n2. BSNES+ special version: https://github.com/binary1230/bsnes-plus/releases !!! NORMAL BSNES+ WONT WORK, YOU MUST USE THIS LINK. grab whatever latest version is here !!!\n\n## Start Capturing\n\n### BSNES Setup\n1. Open BSNES, Tools -> Debugger, check the 'Trace' box. \n2. BSNES will start listening on port 27015 and may appear to freeze (this is normal, though, crappy- sorry) until Diztinguish connects.\n\n### Diz Setup\n4. In Diztinguish, either open an existing project or import the same ROM file, and save your project before continuing.\n5. In Diz, Click Tools -> Live Capture -> BSNESPlus Trace Logging\n6. In the dialog that pops up, click connect and BSNES will unfreeze. It's working if the numbers in the GUI start updating.\n\nBSNES will send data over socket connection to Diz, and you can save all that. Pretty fun.  Diz will mark M and A flags, and whether bytes are opcodes or instructions.\n\nNote that the connecting/disconnecting process with BSNES is kind of janky right now, you may have to kill BSNES after you are done capturing, or to get it to listen \non the socket properly again.\n\nI recommend saving after every capture, and stopping and starting for longer playthroughs, just to be safe.\n\n## Optional extra tools\n1. If you have errors with assembling the exported assembly code from Diz with Asar, there's a fix for Asar for relative addressing on branches that's needed for some ROMS. [asar-domfix--github-issue-170--05-06-2021.zip](https://github.com/Dotsarecool/DiztinGUIsh/files/6432707/asar-domfix--github-issue-170--05-06-2021.zip)\n\n## Visualizer\n\nOnce you have the other stuff working smoothly, there's an optional visualization mode that shows the live capture.\n\nIn DiztinGUIsh, before you open the capture dialog, click Tools -> Visual Map. Leave that up while you run the tracelog and watch your ROM get filled in.\n\nThis is a sped up video of about a 1 minute tracelog capture run (full version is here: https://www.youtube.com/watch?v=NCZUESf82Rg&feature=youtu.be)\n\n![ezgif com-gif-maker](https://user-images.githubusercontent.com/5413064/97286056-69033900-1819-11eb-925d-67e1bbce95a7.gif)\n\n## Troubleshooting\n\nFirst, apologies. The whole capture process is a little unfriendly and finicky. We need to improve user-friendliness here.\n\nFor network connectivitiy issues (like \"Connection refused\"), try this:\n\n1. Are you running iTunes by any chance? it seems to sometimes use Port 27015 (TCP). Disable it if so.\n2. Press Windows Key + X, PowerShell Admin\n3. Close BSNES and Diz, run the following commands:\n4. ```netsh int ip show excludedportrange protocol=tcp``` to show reserved ports on your system. If you see a range of ports that overlaps with 27015, you must address this first.\n5. ```netstat -abn``` to look for other apps using port 27015 (like iTunes).  If you see anything, close that application.\n6. Start just BSNES, Tools -> Debugger, check the 'Trace' box.   Run ```netstat -abn``` and look for port 27015 again, you should see BSNES in that list. If you don't, the issue is something wrong with BSNES not listening.\n7. Check windows firewall for any exclusions for port 27015\n8. If it still doesn't work, vist us in Discord in #diztinguish in the SnesLab discord.\n\n# Extra tech info\n\n## Notes\n\nThis has only been tested on localhost, I don't recommend you run it on a real ethernet network, it's not tuned for that, MTU might be crazy (would be kinda fun to see if it works)\n\nTip: If things are running too slow, in BSNESplus click the 'trace mask' button, which will filter areas that were already visited.\n\nPlease make a backup of your file if you have done significant disassembly. It should be pretty safe but, you never know.\n\nFor max fun results, export the disassembly before running the tracelog, then commit the export into a git repository.  Then, run the tracelog, and view the diff.  \nYou should hopefully see lots of the game code being revealed. Pretty Great!\n\nCurrently we only support 65816 importing, but, in the future might be able to add support for SPC, SA-1, etc.\n\n## More stuff to try\n\nTry also using BSNES-plus's 'usage map' feature, which is complementary to capturing. It can mark ROM sections are read, written, or executed.\n\nIn https://bsnes.revenant1.net/documentation.html look up the section about \"Cache memory usage table to disk\"\nDiztinguish can take this file (a .bin file) and import it via File -> Import -> BSNES Usage Map\n\n## Bonus tip: how to share / merge / diff Diz project files via git\n\nBy default, Diz will output a ```.diz``` file, which under the hood is an ```.xml.gz``` file.  That's fine, but if you are trying to collaborate on a disasembly project with other folks, it is helpful to work without the compression in the underlying XML-based format.  Diz's XML format was explicitly designed for ease of merging/diff while still maintaining a compact file size.\n\nTo work this way, simply save your project in Diz with the extension ```.dizraw```, and it will write your project in plain text/XML automatically.\n\nYou can check that project file into a git repository and collaborate with others.\n\n# thanks \n@gocha for the initial work on this awesome trace system! @dotsarecool for the awesome tool\n\nAny questions, come to the #diztinguish channel in the SNESLab Discord,\nor also the Retro Game Mechanics Explained Discord https://discord.me/rgmechex, in the #your-tech-projects channel.\n"
  },
  {
    "path": "contributing.md",
    "content": "You'll need:\n- Windows 10+\n- .NET SDK 8 or higher\n- VS2022, or another IDE with C# support such as JetBrains Rider or VS Code\n- Git for Windows, which is available via the Visual Studio Installer\n"
  },
  {
    "path": "global.json",
    "content": "{\n  \"sdk\": {\n    \"version\": \"6.0\",\n    \"rollForward\": \"latestMajor\",\n    \"allowPrerelease\": true\n  }\n}"
  }
]