├── .appveyor.yml ├── .bandit ├── .codacy.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .license_index.txt ├── CHANGELOG.md ├── LICENSE ├── README.md ├── THIRD_PARTY_NOTICES.md ├── VERSION ├── dotnet_component_interface ├── ComponentInterfaceVersionAttribute.cs ├── IComponent.cs ├── IConfigVar.cs ├── IConsole.cs ├── IControls.cs ├── IDynamicServicesManager.cs ├── IFileInfo.cs ├── IMainMenu.cs ├── IMetadbHandle.cs ├── IPlaybackCallbacks.cs ├── IPlaybackControls.cs ├── IPreferencesPage.cs ├── IStaticServicesManager.cs ├── ITitleFormat.cs ├── IUtils.cs ├── Properties │ └── launchSettings.json ├── RequiresInitializationAttribute.cs └── dotnet_component_interface.csproj ├── dotnet_test ├── Form1.cs ├── Form1.resx ├── Properties │ └── launchSettings.json ├── dotnet_test.csproj └── test.cs ├── foo_dotnet_component_host ├── .clang-format ├── acfu │ ├── acfu_github.h │ ├── acfu_registration.cpp │ ├── acfu_registration.h │ ├── semantic_version.cpp │ └── semantic_version.h ├── component_defines.h ├── component_guids.h ├── component_main.cpp ├── component_paths.cpp ├── component_paths.h ├── convert │ ├── to_native.cpp │ ├── to_native.h │ ├── to_net.cpp │ └── to_net.h ├── fb2k │ ├── component_registration.cpp │ ├── component_registration.h │ ├── init_quit.cpp │ ├── main.cpp │ ├── main_menu.cpp │ ├── main_menu.h │ ├── play_callback.cpp │ ├── play_callback.h │ ├── preferences_pages.cpp │ └── preferences_pages.h ├── foo_dotnet_component_host.filters ├── foo_dotnet_component_host.vcxproj ├── foo_dotnet_component_host.vcxproj.filters ├── host │ ├── component.h │ ├── delayed_installation.cpp │ ├── delayed_installation.h │ ├── fb2k_controls.cpp │ ├── fb2k_controls.h │ ├── fb2k_services.cpp │ ├── fb2k_services.h │ ├── fb2k_utils.cpp │ ├── fb2k_utils.h │ ├── host.cpp │ └── host.h ├── loader │ ├── assembly_loader.cpp │ ├── assembly_loader.h │ ├── component_loader.cpp │ └── component_loader.h ├── net_objects │ ├── fb_config_var.cpp │ ├── fb_config_var.h │ ├── fb_console.cpp │ ├── fb_console.h │ ├── fb_file_info.cpp │ ├── fb_file_info.h │ ├── fb_main_menu.cpp │ ├── fb_main_menu.h │ ├── fb_metadb_handle.cpp │ ├── fb_metadb_handle.h │ ├── fb_play_callback.cpp │ ├── fb_play_callback.h │ ├── fb_play_control.cpp │ ├── fb_play_control.h │ ├── fb_preferences_page_callback.cpp │ ├── fb_preferences_page_callback.h │ ├── fb_title_format.cpp │ ├── fb_title_format.h │ └── impl │ │ └── enumerable_impl.h ├── net_utils │ └── exception_generator.h ├── packages.config ├── resources │ ├── foo_dotnet_component_host.rc │ ├── resource.h │ └── version.rc ├── stdafx.cpp ├── stdafx.h ├── ui │ ├── ui_preferences.cpp │ ├── ui_preferences.h │ ├── ui_preferences_form.cpp │ ├── ui_preferences_form.h │ └── ui_preferences_form.resx └── utils │ ├── delayed_log.cpp │ ├── delayed_log.h │ ├── menu_helpers.cpp │ └── menu_helpers.h ├── licenses ├── JSON for Modern C++.txt ├── PFC.txt ├── fmt.txt ├── foobar2000 SDK.txt └── range-v3.txt ├── props └── submodules │ ├── fb2k_utils.props │ └── submodules.props ├── scripts ├── README.md ├── call_wrapper.py ├── download_submodules.py ├── generate_package_info.py ├── pack_component.py ├── patch_submodules.py ├── patches │ ├── foobar2000.patch │ └── pfc.patch ├── publish_interface_package.py ├── setup.py └── update_gh_pages.py └── workspaces └── foo_dotnet_component_host.sln /.appveyor.yml: -------------------------------------------------------------------------------- 1 | 2 | #---------------------------------# 3 | # general configuration # 4 | #---------------------------------# 5 | 6 | # version format 7 | version: 1.0.{build}-{branch} 8 | 9 | # branches to build 10 | branches: 11 | # blacklist 12 | except: 13 | - gh-pages 14 | 15 | # Skipping commits affecting specific files (GitHub only). More details here: /docs/appveyor-yml 16 | skip_commits: 17 | # Default `skip` messages are applied even on tag builds with APPVEYOR_IGNORE_COMMIT_FILTERING_ON_TAG==true 18 | message: /\[_skip ci_\]|\[_ci skip_\]|\[_skip_ci_\]|\[_ci_skip_\]/ 19 | files: 20 | - README.md 21 | - THIRD_PARTY_NOTICES.md 22 | # - docs/* 23 | # - '**/*.png' 24 | # - '**/*.jpg' 25 | # - '**/*.jpeg' 26 | # - '**/*.bmp' 27 | # - '**/*.gif' 28 | # - '**/*.js' 29 | # - '**/*.txt' 30 | # - '**/*.md' 31 | 32 | # Maximum number of concurrent jobs for the project 33 | max_jobs: 1 34 | 35 | #---------------------------------# 36 | # environment configuration # 37 | #---------------------------------# 38 | 39 | # Build worker image (VM template) 40 | image: Visual Studio 2019 41 | 42 | # environment variables 43 | environment: 44 | APPVEYOR_IGNORE_COMMIT_FILTERING_ON_TAG: true 45 | git_user_email: qwertiest@mail.ru 46 | git_user_name: TheQwertiest 47 | GITHUB_TOKEN: 48 | secure: tGGDBtpW74kNoY4PwFw2uuRCj/Npr9kDYaJTdm3IFOcEXA1dr8pc9vgur+6rvaoq 49 | 50 | # this is how to allow failing jobs in the matrix 51 | matrix: 52 | fast_finish: true # set this flag to immediately finish build once one of the jobs fails. 53 | 54 | cache: 55 | - workspaces\packages -> **\packages.config 56 | 57 | # scripts that run after cloning repository 58 | install: 59 | # GitHub CLI 60 | - choco install gh -y 61 | - refreshenv 62 | - gh config set -h github.com git_protocol https 63 | # PIP 64 | - py -3 -m pip install semver 65 | # Component 66 | - py -u scripts\setup.py 67 | 68 | #---------------------------------# 69 | # build configuration # 70 | #---------------------------------# 71 | 72 | # build platform, i.e. x86, x64, Any CPU. This setting is optional. 73 | platform: 74 | - Win32 75 | 76 | # build Configuration, i.e. Debug, Release, etc. 77 | configuration: 78 | - Debug 79 | - Release 80 | 81 | # Build settings, not to be confused with "before_build" and "after_build". 82 | # "project" is relative to the original build directory and not influenced by directory changes in "before_build". 83 | build: 84 | parallel: true # enable MSBuild parallel builds 85 | project: workspaces\foo_dotnet_component_host.sln # path to Visual Studio solution or project 86 | 87 | # MSBuild verbosity level 88 | verbosity: normal 89 | 90 | # scripts to run before build 91 | before_build: 92 | - nuget restore workspaces\foo_dotnet_component_host.sln 93 | 94 | # to run your custom scripts instead of automatic MSBuild 95 | build_script: 96 | 97 | # scripts to run after build (working directory and environment changes are persisted from the previous steps) 98 | after_build: 99 | 100 | # scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services) 101 | before_package: 102 | - if '%configuration%' == 'Debug' ( 103 | py -u scripts\pack_component.py --debug && 104 | ren "_result\%platform%_%configuration%\foo_dotnet_component_host.fb2k-component" "foo_dotnet_component_host.fb2k-component_debug" 105 | ) 106 | else ( 107 | py -u scripts\pack_component.py 108 | ) 109 | 110 | # to disable automatic builds 111 | #build: off 112 | 113 | #---------------------------------# 114 | # artifacts configuration # 115 | #---------------------------------# 116 | 117 | artifacts: 118 | # pushing a single file with environment variable in path and "Deployment name" specified 119 | - path: _result\$(platform)_$(configuration)\foo_dotnet_component_host.fb2k-component 120 | name: DNET package (Release) 121 | - path: _result\$(platform)_$(configuration)\foo_dotnet_component_host_pdb.zip 122 | name: DNET PDB package (Release) 123 | - path: _result\$(platform)_$(configuration)\foo_dotnet_component_host.fb2k-component_debug 124 | name: DNET package (Debug) 125 | 126 | #---------------------------------# 127 | # deployment configuration # 128 | #---------------------------------# 129 | 130 | # providers: Local, FTP, WebDeploy, AzureCS, AzureBlob, S3, NuGet, Environment 131 | # provider names are case-sensitive! 132 | deploy: 133 | description: 'Dummy description' 134 | provider: GitHub 135 | auth_token: $(GITHUB_TOKEN) 136 | artifact: /foo_dotnet_component_host.*/ # upload all NuGet packages to release assets 137 | prerelease: true 138 | on: 139 | branch: master # release from master branch only 140 | appveyor_repo_tag: true # deploy on tag push only 141 | 142 | 143 | # scripts to run before deployment 144 | before_deploy: 145 | 146 | # scripts to run after deployment 147 | after_deploy: 148 | 149 | # to run your custom scripts instead of provider deployments 150 | deploy_script: 151 | 152 | # to disable deployment 153 | # deploy: off 154 | 155 | #---------------------------------# 156 | # global handlers # 157 | #---------------------------------# 158 | 159 | # on successful build 160 | on_success: 161 | # issues clean up 162 | - ps: | 163 | If ($env:APPVEYOR_REPO_TAG -eq $true -and $env:CONFIGURATION -eq "Release") 164 | { 165 | py -u submodules\fb2k_utils\scripts\close_gh_issues.py 166 | } 167 | # gh-pages 168 | - ps: | 169 | If ($env:APPVEYOR_REPO_TAG -eq $true -and $env:CONFIGURATION -eq "Release") 170 | { 171 | git config --global credential.helper store 172 | Add-Content "$env:USERPROFILE\.git-credentials" "https://$($env:GITHUB_TOKEN):x-oauth-basic@github.com`n" 173 | git config --global user.email $env:git_user_email 174 | git config --global user.name $env:git_user_name 175 | git clone -q --depth=1 -b gh-pages https://github.com/$($env:APPVEYOR_REPO_NAME).git gh-pages 176 | py -u scripts\update_gh_pages.py 177 | cd gh-pages 178 | py -u build_scripts\update_layout.py 179 | py -u build_scripts\generate_third_party.py 180 | git add -A 2>&1 181 | git commit -q -m "Updated documentation: $env:APPVEYOR_REPO_TAG_NAME ($env:APPVEYOR_REPO_COMMIT)" 182 | git push -q origin gh-pages 183 | cd $env:APPVEYOR_BUILD_FOLDER 184 | } 185 | 186 | # on build failure 187 | on_failure: 188 | 189 | # after build failure or success 190 | on_finish: 191 | 192 | #---------------------------------# 193 | # notifications # 194 | #---------------------------------# 195 | 196 | notifications: 197 | -------------------------------------------------------------------------------- /.bandit: -------------------------------------------------------------------------------- 1 | skips: ['B101','B404','B602','B607'] 2 | -------------------------------------------------------------------------------- /.codacy.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | prospector: 3 | enabled: true 4 | python_version: 3 5 | pylint: 6 | enabled: true 7 | python_version: 3 8 | exclude_paths: 9 | - 'libspotify/**' 10 | - 'submodules/**' -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | # Custom for Visual Studio 4 | *.cs diff=csharp 5 | # Standard to msysgit 6 | *.doc diff=astextplain 7 | *.DOC diff=astextplain 8 | *.docx diff=astextplain 9 | *.DOCX diff=astextplain 10 | *.dot diff=astextplain 11 | *.DOT diff=astextplain 12 | *.pdf diff=astextplain 13 | *.PDF diff=astextplain 14 | *.rtf diff=astextplain 15 | *.RTF diff=astextplain 16 | 17 | component_data/** linguist-detectable=false 18 | scripts/** linguist-detectable=false 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /mozjs/* 2 | 3 | # Created by https://www.gitignore.io/api/visualstudio 4 | 5 | ### VisualStudio ### 6 | ## Ignore Visual Studio temporary files, build results, and 7 | ## files generated by popular Visual Studio add-ons. 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | /_* 20 | *.fb2k-component 21 | [Dd]ebug/ 22 | [Dd]ebugPublic/ 23 | [Dd]ebug FB2K/ 24 | [Rr]elease/ 25 | [Rr]eleases/ 26 | [Rr]elease FB2K/ 27 | x64/ 28 | x86/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | 34 | # Visual Studio 2015 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # MSTest test Results 40 | [Tt]est[Rr]esult*/ 41 | [Bb]uild[Ll]og.* 42 | 43 | # NUNIT 44 | *.VisualState.xml 45 | TestResult.xml 46 | 47 | # Build Results of an ATL Project 48 | [Dd]ebugPS/ 49 | [Rr]eleasePS/ 50 | dlldata.c 51 | 52 | # DNX 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | *_i.c 58 | *_p.c 59 | *_i.h 60 | *.ilk 61 | *.meta 62 | *.obj 63 | *.pch 64 | *.pdb 65 | *.pgc 66 | *.pgd 67 | *.rsp 68 | *.sbr 69 | *.tlb 70 | *.tli 71 | *.tlh 72 | *.tmp 73 | *.tmp_proj 74 | *.log 75 | *.vspscc 76 | *.vssscc 77 | .builds 78 | *.pidb 79 | *.svclog 80 | *.scc 81 | 82 | # Chutzpah Test files 83 | _Chutzpah* 84 | 85 | # Visual C++ cache files 86 | ipch/ 87 | *.aps 88 | *.ncb 89 | *.opendb 90 | *.opensdf 91 | *.sdf 92 | *.cachefile 93 | *.VC.db 94 | *.VC.VC.opendb 95 | 96 | # Visual Studio profiler 97 | *.psess 98 | *.vsp 99 | *.vspx 100 | *.sap 101 | 102 | # TFS 2012 Local Workspace 103 | $tf/ 104 | 105 | # Guidance Automation Toolkit 106 | *.gpState 107 | 108 | # ReSharper is a .NET coding add-in 109 | _ReSharper*/ 110 | *.[Rr]e[Ss]harper 111 | *.DotSettings.user 112 | 113 | # JustCode is a .NET coding add-in 114 | .JustCode 115 | 116 | # TeamCity is a build add-in 117 | _TeamCity* 118 | 119 | # DotCover is a Code Coverage Tool 120 | *.dotCover 121 | 122 | # Visual Studio code coverage results 123 | *.coverage 124 | *.coveragexml 125 | 126 | # NCrunch 127 | _NCrunch_* 128 | .*crunch*.local.xml 129 | nCrunchTemp_* 130 | 131 | # MightyMoose 132 | *.mm.* 133 | AutoTest.Net/ 134 | 135 | # Web workbench (sass) 136 | .sass-cache/ 137 | 138 | # Installshield output folder 139 | [Ee]xpress/ 140 | 141 | # DocProject is a documentation generator add-in 142 | DocProject/buildhelp/ 143 | DocProject/Help/*.HxT 144 | DocProject/Help/*.HxC 145 | DocProject/Help/*.hhc 146 | DocProject/Help/*.hhk 147 | DocProject/Help/*.hhp 148 | DocProject/Help/Html2 149 | DocProject/Help/html 150 | 151 | # Click-Once directory 152 | publish/ 153 | 154 | # Publish Web Output 155 | *.[Pp]ublish.xml 156 | *.azurePubxml 157 | # TODO: Comment the next line if you want to checkin your web deploy settings 158 | # but database connection strings (with potential passwords) will be unencrypted 159 | *.pubxml 160 | *.publishproj 161 | 162 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 163 | # checkin your Azure Web App publish settings, but sensitive information contained 164 | # in these scripts will be unencrypted 165 | PublishScripts/ 166 | 167 | # NuGet Packages 168 | *.nupkg 169 | # The packages folder can be ignored because of Package Restore 170 | **/packages/* 171 | # except build/, which is used as an MSBuild target. 172 | !**/packages/build/ 173 | # Uncomment if necessary however generally it will be regenerated when needed 174 | #!**/packages/repositories.config 175 | # NuGet v3's project.json files produces more ignoreable files 176 | *.nuget.props 177 | *.nuget.targets 178 | 179 | # Microsoft Azure Build Output 180 | csx/ 181 | *.build.csdef 182 | 183 | # Microsoft Azure Emulator 184 | ecf/ 185 | rcf/ 186 | 187 | # Windows Store app package directories and files 188 | AppPackages/ 189 | BundleArtifacts/ 190 | Package.StoreAssociation.xml 191 | _pkginfo.txt 192 | 193 | # Visual Studio cache files 194 | # files ending in .cache can be ignored 195 | *.[Cc]ache 196 | # but keep track of directories ending in .cache 197 | !*.[Cc]ache/ 198 | 199 | # Others 200 | ClientBin/ 201 | ~$* 202 | *~ 203 | *.dbmdl 204 | *.dbproj.schemaview 205 | *.jfm 206 | *.pfx 207 | *.publishsettings 208 | node_modules/ 209 | orleans.codegen.cs 210 | 211 | # Since there are multiple workflows, uncomment next line to ignore bower_components 212 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 213 | #bower_components/ 214 | 215 | # RIA/Silverlight projects 216 | Generated_Code/ 217 | 218 | # Backup & report files from converting an old project file 219 | # to a newer Visual Studio version. Backup files are not needed, 220 | # because we have git ;-) 221 | _UpgradeReport_Files/ 222 | Backup*/ 223 | UpgradeLog*.XML 224 | UpgradeLog*.htm 225 | 226 | # SQL Server files 227 | *.mdf 228 | *.ldf 229 | 230 | # Business Intelligence projects 231 | *.rdl.data 232 | *.bim.layout 233 | *.bim_*.settings 234 | 235 | # Microsoft Fakes 236 | FakesAssemblies/ 237 | 238 | # GhostDoc plugin setting file 239 | *.GhostDoc.xml 240 | 241 | # Node.js Tools for Visual Studio 242 | .ntvs_analysis.dat 243 | 244 | # Visual Studio 6 build log 245 | *.plg 246 | 247 | # Visual Studio 6 workspace options file 248 | *.opt 249 | 250 | # Visual Studio LightSwitch build output 251 | **/*.HTMLClient/GeneratedArtifacts 252 | **/*.DesktopClient/GeneratedArtifacts 253 | **/*.DesktopClient/ModelManifest.xml 254 | **/*.Server/GeneratedArtifacts 255 | **/*.Server/ModelManifest.xml 256 | _Pvt_Extensions 257 | 258 | # Paket dependency manager 259 | .paket/paket.exe 260 | paket-files/ 261 | 262 | # FAKE - F# Make 263 | .fake/ 264 | 265 | # JetBrains Rider 266 | .idea/ 267 | *.sln.iml 268 | 269 | # CodeRush 270 | .cr/ 271 | 272 | # Python Tools for Visual Studio (PTVS) 273 | __pycache__/ 274 | *.pyc 275 | 276 | ### VisualStudio Patch ### 277 | build/ 278 | 279 | ### Exceptions ### 280 | !/component/**/packages/* 281 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/acfu-sdk"] 2 | path = submodules/acfu-sdk 3 | url = https://github.com/3dyd/acfu-sdk.git 4 | [submodule "submodules/fmt"] 5 | path = submodules/fmt 6 | url = https://github.com/fmtlib/fmt.git 7 | [submodule "submodules/fb2k_utils"] 8 | path = submodules/fb2k_utils 9 | url = https://github.com/TheQwertiest/fb2k_utils.git 10 | [submodule "submodules/foobar2000"] 11 | path = submodules/foobar2000 12 | url = https://github.com/TheQwertiest/foobar2000-sdk.git 13 | [submodule "submodules/pfc"] 14 | path = submodules/pfc 15 | url = https://github.com/TheQwertiest/pfc.git 16 | [submodule "submodules/json"] 17 | path = submodules/json 18 | url = https://github.com/nlohmann/json.git 19 | -------------------------------------------------------------------------------- /.license_index.txt: -------------------------------------------------------------------------------- 1 | foobar2000 SDK: other 2 | fmt: other 3 | JSON for Modern C++: MIT 4 | range-v3: BSL-1.0 5 | PFC: zlib 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | #### Table of Contents 4 | - [Unreleased](#unreleased) 5 | - [1.0.0](#100---2021-10-20) 6 | 7 | ___ 8 | 9 | ## [Unreleased][] 10 | 11 | ## [1.0.0][] - 2021-10-20 12 | 13 | Initial release. 14 | 15 | [unreleased]: https://github.com/TheQwertiest/foo_dotnet_component_host/compare/v1.0.0...HEAD 16 | [1.0.0]: https://github.com/TheQwertiest/foo_dotnet_component_host/commits/master 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 TheQwertiest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET Component Host 2 | [![version][version_badge]][changelog] [![Build status][appveyor_badge]](https://ci.appveyor.com/project/TheQwertiest/foo-dotnet-component-host/branch/master) [![CodeFactor][codefactor_badge]](https://www.codefactor.io/repository/github/theqwertiest/foo_dotnet_component_host/overview/master) [![Codacy Badge][codacy_badge]](https://app.codacy.com/app/qwertiest/foo_dotnet_component_host?utm_source=github.com&utm_medium=referral&utm_content=TheQwertiest/foo_dotnet_component_host&utm_campaign=Badge_Grade_Dashboard) 3 | 4 | This is a component for the [foobar2000](https://www.foobar2000.org) audio player that acts as a host for components written in C#. 5 | 6 | Visit [component homepage](https://theqwertiest.github.io/foo_dotnet_component_host/) for more info. 7 | 8 | [changelog]: CHANGELOG.md 9 | [version_badge]: https://img.shields.io/github/release/theqwertiest/foo_dotnet_component_host.svg 10 | [appveyor_badge]: https://ci.appveyor.com/api/projects/status/gq3d890wecgf2hfu/branch/master?svg=true 11 | [codacy_badge]: https://app.codacy.com/project/badge/Grade/d759ad76bb0d48b88ad0d5a397a4a7ed 12 | [codefactor_badge]: https://www.codefactor.io/repository/github/theqwertiest/foo_dotnet_component_host/badge/master 13 | -------------------------------------------------------------------------------- /THIRD_PARTY_NOTICES.md: -------------------------------------------------------------------------------- 1 | .NET Component Host uses third-party libraries or other resources that may 2 | be distributed under licenses different than the Spider Monkey Panel software. 3 | The linked notices are provided for information only. 4 | 5 | - [foobar2000 SDK - other](licenses/foobar2000%20SDK.txt) 6 | - [fmt - other](licenses/fmt.txt) 7 | - [JSON for Modern C++ - MIT](licenses/JSON%20for%20Modern%20C%2B%2B.txt) 8 | - [range-v3 - BSL-1.0](licenses/range-v3.txt) 9 | - [PFC - zlib](licenses/PFC.txt) 10 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.0.1-dev 2 | -------------------------------------------------------------------------------- /dotnet_component_interface/ComponentInterfaceVersionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | [AttributeUsage(AttributeTargets.Constructor)] 6 | public class ComponentInterfaceVersionAttribute : Attribute 7 | { 8 | public readonly Version Version; 9 | public ComponentInterfaceVersionAttribute(string version) 10 | { 11 | this.Version = new Version(version); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | public struct ComponentInfo 6 | { 7 | public string Name; 8 | public string Version; 9 | public string Description; 10 | } 11 | 12 | public interface IComponent 13 | { 14 | ComponentInfo GetInfo(); 15 | void Initialize(IStaticServicesManager servicesManager, IUtils utils); 16 | void Start(IDynamicServicesManager servicesManager, IControls controls); 17 | void Shutdown(); 18 | } 19 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IConfigVar.cs: -------------------------------------------------------------------------------- 1 | namespace Qwr.ComponentInterface 2 | { 3 | public interface IConfigVar 4 | { 5 | void Set(T value); 6 | T Get(); 7 | } 8 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IConsole.cs: -------------------------------------------------------------------------------- 1 | namespace Qwr.ComponentInterface 2 | { 3 | public interface IConsole 4 | { 5 | void Log(string text); 6 | } 7 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IControls.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | public interface IControls 6 | { 7 | IConsole Console(); 8 | void ExecuteMainMenuCommand(string command); 9 | void ExecuteContextMenuCommand(string command, List? metadbHandles = null); 10 | IPlaybackControls PlaybackControls(); 11 | ITitleFormat TitleFormat(string expression); 12 | } 13 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IDynamicServicesManager.cs: -------------------------------------------------------------------------------- 1 | namespace Qwr.ComponentInterface 2 | { 3 | public interface IDynamicServicesManager 4 | { 5 | IPlaybackCallbacks RegisterForPlaybackCallbacks(); 6 | } 7 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IFileInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Qwr.ComponentInterface 5 | { 6 | public struct FileInfoInfo 7 | { 8 | public string Name; 9 | public string Value; 10 | } 11 | 12 | public struct FileInfoMeta 13 | { 14 | public string Name; 15 | public IEnumerable Values; 16 | } 17 | 18 | public interface IFileInfo : IDisposable 19 | { 20 | IEnumerable InfoEnum(); 21 | IEnumerable MetaEnum(); 22 | } 23 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IMainMenu.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | public interface IMainMenuCommand 6 | { 7 | bool IsEnabled { get; set; } 8 | bool IsDefaultHidden { get; set; } 9 | bool IsAlwaysHidden { get; set; } 10 | bool IsChecked { get; set; } 11 | bool IsRadioChecked { get; set; } 12 | } 13 | 14 | public interface IMainMenuCommandSection 15 | { 16 | IMainMenuCommand AddCommand(Guid guid, string name, string description, 17 | Action action); 18 | } 19 | 20 | public interface IMainMenuGroup 21 | { 22 | IMainMenuGroup AddGroup(Guid guid, string name, ushort? sortPriority = null, 23 | bool isPopup = true); 24 | IMainMenuCommandSection AddCommandSection(ushort? sortPriority = null); 25 | } 26 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IMetadbHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace Qwr.ComponentInterface 5 | { 6 | public enum ArtId 7 | { 8 | CoverFront = 0, 9 | CoverBack = 1, 10 | Disc = 2, 11 | Icon = 3, 12 | Artist = 4 13 | } 14 | 15 | public interface IMetadbHandle : IDisposable 16 | { 17 | Bitmap Artwork(ArtId artId); 18 | Bitmap ArtworkStub(ArtId artId); 19 | double Length(); 20 | string Path(); 21 | IFileInfo FileInfo(); 22 | } 23 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IPlaybackCallbacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | public enum PlaybackStartCommand 6 | { 7 | Default = 0, 8 | Play = 1, 9 | Next = 2, 10 | Previous = 3, 11 | Random = 5 12 | } 13 | 14 | public enum PlaybackStopReason 15 | { 16 | User = 0, 17 | EndOfFile = 1, 18 | StartingAnother = 2, 19 | ShuttingDown = 3, 20 | } 21 | 22 | public class GenericEventArgs : EventArgs 23 | { 24 | public T Value { get; private set; } 25 | 26 | public GenericEventArgs(T Value) { this.Value = Value; } 27 | } 28 | 29 | public class PlaybackStartingEventArgs : EventArgs 30 | { 31 | public readonly PlaybackStartCommand StartCommand; 32 | public readonly bool WasPaused; 33 | 34 | public PlaybackStartingEventArgs(PlaybackStartCommand startCommand, 35 | bool wasPaused) 36 | { 37 | StartCommand = startCommand; 38 | WasPaused = wasPaused; 39 | } 40 | } 41 | 42 | public interface IPlaybackCallbacks 43 | { 44 | event EventHandler> PlaybackAdvancedToNewTrack; 45 | event EventHandler PlaybackStarting; 46 | event EventHandler> PlaybackStopped; 47 | event EventHandler> PlaybackPausedStateChanged; 48 | event EventHandler DynamicPlaybackTrackInfoChanged; 49 | event EventHandler DynamicTrackInfoChanged; 50 | event EventHandler> CurrentTrackInfoChanged; 51 | event EventHandler> TrackPlaybackPositionChanged; 52 | event EventHandler> TrackSeekPerformed; 53 | event EventHandler> VolumeChanged; 54 | } 55 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IPlaybackControls.cs: -------------------------------------------------------------------------------- 1 | namespace Qwr.ComponentInterface 2 | { 3 | public interface IPlaybackControls 4 | { 5 | IMetadbHandle NowPlaying(); 6 | double TrackPlaybackPosition(); 7 | bool IsPlaying(); 8 | bool IsPaused(); 9 | void Play(); 10 | void Pause(); 11 | void Next(); 12 | void Prev(); 13 | } 14 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IPreferencesPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | 6 | [Flags] 7 | public enum PreferencesPageState : UInt32 8 | { 9 | HasNoChanges = 0, 10 | HasChanged = 1, 11 | IsResettable = 8, 12 | NeedsFb2kRestart = 2, 13 | NeedsLibraryRescan = 32, 14 | NeedsPlaybackRestart = 4, 15 | } 16 | public struct PreferencesPageInfo 17 | { 18 | public Guid Guid; 19 | public Guid ParentGuid; 20 | public string Name; 21 | public string? HelpUrl; 22 | } 23 | public interface IPreferencesPageCallback 24 | { 25 | void OnStateChanged(); 26 | } 27 | 28 | public interface IPreferencesPage 29 | { 30 | void Initialize(IntPtr parentHandle, IPreferencesPageCallback callback); 31 | 32 | PreferencesPageState State(); 33 | IntPtr Handle(); 34 | void Apply(); 35 | void Reset(); 36 | } 37 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IStaticServicesManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | /// 6 | /// Methods of this interface can only be invoked inside `IComponent.Initialize` call 7 | /// 8 | public interface IStaticServicesManager 9 | { 10 | IMainMenuGroup GetMainMenuGroup(Guid mainMenuGroupGuid); 11 | void RegisterPreferencesPage(PreferencesPageInfo preferencesPageInfo, 12 | Type preferencePageType); 13 | IConfigVar RegisterConfigVar(Guid cfgGuid, T defaultValue); 14 | 15 | /// 16 | /// This will only work if `foo_acfu` component is installed (but it's safe to call even if it's not) 17 | /// 18 | void RegisterAcfu(Guid guid, string componentRepo, string repoOwner); 19 | 20 | // TODO: implement dynamic main menu here 21 | // IMainMenuNode RegisterMainMenuRoot(MainMenuItemInfo info, Guid 22 | // parentGuid); 23 | } 24 | } -------------------------------------------------------------------------------- /dotnet_component_interface/ITitleFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | public interface ITitleFormat : IDisposable 6 | { 7 | string Eval(bool force = false); 8 | string EvalWithMetadb(IMetadbHandle metadbHandle); 9 | } 10 | } -------------------------------------------------------------------------------- /dotnet_component_interface/IUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace Qwr.ComponentInterface 5 | { 6 | public enum Fb2kGuidId 7 | { 8 | PrefPage_Tagging, 9 | PrefPage_Root, 10 | PrefPage_Tools, 11 | PrefPage_Display, 12 | PrefPage_Playback, 13 | PrefPage_Visualizations, 14 | PrefPage_Input, 15 | PrefPage_Core, 16 | PrefPage_TagWriting, 17 | PrefPage_MediaLibrary, 18 | PrefPage_Output, 19 | PrefPage_Advanced, 20 | PrefPage_Components, 21 | MainMenuGroups_File, 22 | MainMenuGroups_Edit, 23 | MainMenuGroups_View, 24 | MainMenuGroups_Playback, 25 | MainMenuGroups_Library, 26 | MainMenuGroups_Help, 27 | } 28 | 29 | public interface IUtils 30 | { 31 | Version HostVersion(); 32 | Guid Fb2kGuid(Fb2kGuidId id); 33 | [RequiresInitialization] 34 | Icon Fb2kIcon(); 35 | string Fb2kPath(); 36 | [RequiresInitialization] 37 | string Fb2kVersion(); 38 | [RequiresInitialization] 39 | bool IsFb2kMinimized(); 40 | string ProfilePath(); 41 | [RequiresInitialization] 42 | void ShowPopup(string text, string title); 43 | } 44 | } -------------------------------------------------------------------------------- /dotnet_component_interface/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "dotnet_sdk_iface": { 4 | "commandName": "Executable", 5 | "executablePath": "X:\\foobar_test\\_scratch\\foobar2000.exe", 6 | "nativeDebugging": true 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /dotnet_component_interface/RequiresInitializationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Qwr.ComponentInterface 4 | { 5 | /// 6 | /// If a method or a class has this attribute, then it means 7 | /// that they should can only be called and used after the component is 8 | /// inititalized 9 | /// 10 | public class RequiresInitializationAttribute : Attribute { } 11 | } -------------------------------------------------------------------------------- /dotnet_component_interface/dotnet_component_interface.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | AnyCPU 9 | enable 10 | dotnet_component_interface 11 | DotnetComponentInterface 12 | TheQwertiest 13 | https://github.com/TheQwertiest/foo_dotnet_component_host 14 | Interface library for `foo_dotnet_component_host` component for foobar2000 player. 15 | Copyright (c) TheQwertiest 2021 16 | MIT 17 | https://github.com/TheQwertiest/foo_dotnet_component_host 18 | git 19 | 20 | true 21 | 0.1.1 22 | 23 | 24 | local.$([System.DateTime]::UtcNow.ToString(yyyyMMdd-HHmm)) 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /dotnet_test/Form1.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | text/microsoft-resx 50 | 51 | 52 | 2.0 53 | 54 | 55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 56 | 57 | 58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 59 | 60 | -------------------------------------------------------------------------------- /dotnet_test/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ClassLibrary1": { 4 | "commandName": "Executable", 5 | "executablePath": "X:\\foobar_test\\_scratch\\foobar2000.exe", 6 | "nativeDebugging": true 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /dotnet_test/dotnet_test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0-windows 5 | dotnet_test 6 | fooTest 7 | true 8 | true 9 | 10 | 11 | 12 | AnyCPU 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | False 23 | runtime 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /dotnet_test/test.cs: -------------------------------------------------------------------------------- 1 | using Qwr.ComponentInterface; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Forms; 5 | 6 | namespace fooTest 7 | { 8 | namespace Custom 9 | { 10 | internal class MyButton : Button { } 11 | } 12 | 13 | public class Test : IComponent 14 | { 15 | IConfigVar _myCfg; 16 | 17 | string initLog; 18 | 19 | [ComponentInterfaceVersion("0.1.1")] 20 | Test() 21 | { 22 | 23 | } 24 | 25 | public void Initialize(IStaticServicesManager servicesManager, IUtils utils) 26 | { 27 | _myCfg = servicesManager.RegisterConfigVar( 28 | Guid.Parse("{627C0767-1234-44f8-8087-BE4934311282}"), "ololo"); 29 | 30 | PreferencesPageInfo info; 31 | info.Name = "1"; 32 | info.Guid = Guid.NewGuid(); 33 | info.ParentGuid = Guid.Parse("{627C0767-0793-44f8-8087-BE4934311282}"); 34 | info.HelpUrl = null; 35 | servicesManager.RegisterPreferencesPage(info, typeof(PrefPage)); 36 | 37 | _utils = utils; 38 | 39 | initLog += utils.Fb2kGuid(Fb2kGuidId.PrefPage_Tagging).ToString() + " "; 40 | initLog += utils.Fb2kPath() + " "; 41 | initLog += utils.HostVersion().ToString() + " "; 42 | 43 | var fileGroup = servicesManager.GetMainMenuGroup( 44 | Guid.Parse("{8F487F1F-419F-47a7-8ECF-EC44AF4449A3}")); 45 | var group = fileGroup.AddGroup(Guid.NewGuid(), "test"); 46 | var cmdSection = group.AddCommandSection(); 47 | var cmd = cmdSection.AddCommand(Guid.NewGuid(), "cmd 0", "azaza", 48 | () => { Log("Lazor!"); }); 49 | cmdSection.AddCommand(Guid.NewGuid(), "cmd 1", "ololo", 50 | () => { Log("Lazor2!"); }); 51 | cmd.IsChecked = true; 52 | cmd.IsDefaultHidden = true; 53 | } 54 | 55 | public void Log(string msg) { _console.Log(msg); } 56 | 57 | public ComponentInfo GetInfo() 58 | { 59 | ComponentInfo info; 60 | info.Name = "Test plugin"; 61 | info.Version = "1.0.0"; 62 | info.Description = "Just a test plugin, really"; 63 | return info; 64 | } 65 | public void Start(IDynamicServicesManager servicesManager, 66 | IControls controls) 67 | { 68 | _console = controls.Console(); 69 | _console.Log("init"); 70 | _console.Log(initLog); 71 | 72 | _console.Log(_utils.ProfilePath().ToString()); 73 | _console.Log(_utils.Fb2kVersion().ToString()); 74 | _console.Log(_utils.IsFb2kMinimized().ToString()); 75 | 76 | _callbacks = servicesManager.RegisterForPlaybackCallbacks(); 77 | 78 | _callbacks.PlaybackAdvancedToNewTrack += (sender, argWrapper) => 79 | { 80 | // var meta = argWrapper.Value; 81 | //_console.Log(controls.TitleFormat("%title%").EvalWithMetadb(meta)); 82 | controls.ExecuteMainMenuCommand("cmd 0"); 83 | }; 84 | } 85 | public void Shutdown() { _console.Log("quit"); } 86 | 87 | private IPlaybackCallbacks _callbacks; 88 | private IConsole _console; 89 | private IUtils _utils; 90 | } 91 | 92 | public class PrefPage : IPreferencesPage 93 | { 94 | [DllImport("user32")] 95 | static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 96 | 97 | public PrefPage() { } 98 | 99 | public void Apply() { } 100 | 101 | IntPtr IPreferencesPage.Handle() { return impl.Handle; } 102 | 103 | public void Initialize(IntPtr parentHandle, 104 | IPreferencesPageCallback callback) 105 | { 106 | impl = new Form1(); 107 | SetParent(impl.Handle, parentHandle); 108 | impl.Anchor = (AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | 109 | AnchorStyles.Bottom); 110 | impl.Show(); 111 | } 112 | 113 | public void Reset() { } 114 | 115 | public PreferencesPageState State() { return 0; } 116 | 117 | private UserControl impl; 118 | } 119 | } -------------------------------------------------------------------------------- /foo_dotnet_component_host/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Mozilla 3 | AccessModifierOffset: '-4' 4 | AlwaysBreakAfterDefinitionReturnType: 'false' 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: None 7 | AlignConsecutiveMacros: Consecutive 8 | AlignEscapedNewlines: Left 9 | AlignOperands: Align 10 | AllowShortBlocksOnASingleLine: 'false' 11 | AllowShortCaseLabelsOnASingleLine: 'false' 12 | AllowShortFunctionsOnASingleLine: None 13 | AllowShortIfStatementsOnASingleLine: 'false' 14 | AllowShortLambdasOnASingleLine: All 15 | AllowShortLoopsOnASingleLine: 'false' 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakTemplateDeclarations: 'true' 18 | BinPackParameters: 'true' 19 | BreakBeforeBinaryOperators: NonAssignment 20 | BreakBeforeBraces: Custom 21 | BraceWrapping: 22 | AfterCaseLabel: 'true' 23 | AfterClass: 'true' 24 | AfterControlStatement: Always 25 | AfterEnum: 'true' 26 | AfterFunction: 'true' 27 | AfterNamespace: 'true' 28 | AfterObjCDeclaration: 'true' 29 | AfterStruct: 'true' 30 | AfterUnion: 'true' 31 | AfterExternBlock: 'true' 32 | BeforeCatch: 'true' 33 | BeforeElse: 'true' 34 | BeforeLambdaBody: 'false' 35 | BeforeWhile: 'false' 36 | IndentBraces: 'false' 37 | SplitEmptyFunction: 'true' 38 | SplitEmptyRecord: 'true' 39 | SplitEmptyNamespace: 'true' 40 | BreakBeforeInheritanceComma: 'true' 41 | BreakConstructorInitializers: BeforeComma 42 | ColumnLimit: '0' 43 | ConstructorInitializerIndentWidth: '4' 44 | ContinuationIndentWidth: '4' 45 | Cpp11BracedListStyle: 'false' 46 | EmptyLineBeforeAccessModifier: Always 47 | FixNamespaceComments: 'true' 48 | IncludeBlocks: Regroup 49 | IncludeCategories: 50 | # precompiled headers 51 | - Regex: '^' 52 | Priority: -1 53 | # "header" 54 | - Regex: '"[[:alnum:]_]+' 55 | Priority: 1 56 | # 57 | - Regex: '^<(acfu-sdk|columns_ui-sdk|fmt|foobar2000|js|nlohmann|nonstd|qwr|range|tim)/' 58 | Priority: 4 59 | # 60 | - Regex: '^<(atl.*\.h|jsapi\.h|jsfriendapi\.h|ILexer.\h|Lexilla.\h|SciLexer\.h|Scintilla\.h)>' 61 | Priority: 5 62 | # | 63 | - Regex: '<[[:alnum:]_]+/.+\.(h|hpp)>' 64 | Priority: 2 65 | # | 66 | - Regex: '<[[:alnum:]_]+\.(h|hpp)>' 67 | Priority: 3 68 | #
69 | - Regex: '<[[:alnum:]_]+>' 70 | Priority: 6 71 | IncludeIsMainRegex: '$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: AfterHash 74 | IndentWidth: '4' 75 | IndentWrappedFunctionNames: false 76 | Language: Cpp 77 | # clang 13+ 78 | # LambdaBodyIndentation: OuterScope 79 | MacroBlockBegin: "^BEGIN_*" 80 | MacroBlockEnd: "^END_*" 81 | MaxEmptyLinesToKeep: '1' 82 | PointerAlignment: Left 83 | ReflowComments: 'true' 84 | SortIncludes: 'false' 85 | SpaceAfterCStyleCast: 'false' 86 | SpaceAfterTemplateKeyword : 'true' 87 | SpaceBeforeAssignmentOperators: 'true' 88 | SpaceBeforeParens: ControlStatements 89 | SpaceBeforeRangeBasedForLoopColon: 'false' 90 | SpaceInEmptyParentheses: 'false' 91 | SpacesInAngles: 'false' 92 | SpacesInCStyleCastParentheses: 'false' 93 | SpacesInParentheses: 'true' 94 | SpacesInSquareBrackets: 'false' 95 | SortIncludes: 'true' 96 | Standard: c++20 97 | TabWidth: '4' 98 | TypenameMacros: ['STDMETHOD', 'STDMETHODIMP_'] 99 | UseTab: Never 100 | 101 | ... 102 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/acfu/acfu_github.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace qwr::acfu 4 | { 5 | 6 | // Ripped from acfu-sdk/utils/github.h: 7 | // replaced rapidjson with nlohmann::json, because don't want to have 8 | // multiple json implementations and additional submodule dependency 9 | 10 | using n_json = nlohmann::json; 11 | 12 | #define QWR_EXPECT_JSON( x ) \ 13 | if ( !( x ) ) \ 14 | throw std::logic_error( "unexpected JSON schema" ) 15 | 16 | class github_latest_release 17 | : public ::acfu::request 18 | { 19 | public: 20 | github_latest_release( const std::string& componentRepo, 21 | const std::string& componentOwner ) 22 | : componentRepo_( componentRepo ) 23 | , componentOwner_( componentOwner ) 24 | { 25 | } 26 | 27 | void run( file_info& info, abort_callback& abort ) override 28 | { 29 | pfc::string8 url = form_releases_url(); 30 | http_request::ptr request = static_api_ptr_t()->create_request( "GET" ); 31 | 32 | for ( service_enum_t<::acfu::authorization> e; !e.finished(); ++e ) 33 | { 34 | e.get()->authorize( url.get_ptr(), request, abort ); 35 | } 36 | 37 | file::ptr response = request->run_ex( url.get_ptr(), abort ); 38 | pfc::array_t data; 39 | response->read_till_eof( data, abort ); 40 | 41 | http_reply::ptr reply; 42 | if ( !response->cast( reply ) ) 43 | { 44 | throw exception_service_extension_not_found(); 45 | } 46 | 47 | n_json doc; 48 | try 49 | { 50 | doc = n_json::parse( std::string{ (const char*)data.get_ptr(), data.get_count() } ); 51 | } 52 | catch ( const n_json::parse_error& err ) 53 | { 54 | throw exception_io_data( PFC_string_formatter() 55 | << "error: " << err.what() ); 56 | } 57 | 58 | pfc::string8 status; 59 | reply->get_status( status ); 60 | // RFC: Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 61 | auto pos = status.find_first( ' ' ); 62 | if ( ~0 == pos || 0 != pfc::strcmp_partial( status.get_ptr() + pos + 1, "200 " ) ) 63 | { 64 | process_error( doc, status.get_ptr() ); 65 | } 66 | else 67 | { 68 | process_response( doc, info ); 69 | } 70 | } 71 | 72 | private: 73 | pfc::string8 form_releases_url() 74 | { 75 | pfc::string8 url; 76 | url << "https://api.github.com/repos/" << componentOwner_.c_str() 77 | << "/" << componentRepo_.c_str() << "/releases/latest"; 78 | return url; 79 | } 80 | 81 | void process_asset( const n_json& asset, file_info& info ) 82 | { 83 | const auto assetRaw = asset.dump(); 84 | info.info_set_ex( "asset", pfc_infinite, assetRaw.c_str(), assetRaw.length() ); 85 | 86 | if ( auto it = asset.find( "browser_download_url" ); it != asset.end() && it->is_string() ) 87 | { 88 | info.meta_set( "download_url", it.value().get().c_str() ); 89 | } 90 | } 91 | 92 | void process_release( const n_json& release, file_info& info ) 93 | { 94 | { 95 | auto it = release.find( "tag_name" ); 96 | QWR_EXPECT_JSON( it != release.end() && it->is_string() ); 97 | info.meta_set( "version", it.value().get().c_str() ); 98 | } 99 | { 100 | auto it = release.find( "html_url" ); 101 | QWR_EXPECT_JSON( it != release.end() && it->is_string() ); 102 | info.meta_set( "download_page", it.value().get().c_str() ); 103 | } 104 | 105 | const auto releaseRaw = release.dump(); 106 | info.info_set_ex( "release", pfc_infinite, releaseRaw.c_str(), releaseRaw.length() ); 107 | } 108 | 109 | void process_response( const n_json& json, file_info& info ) 110 | { 111 | QWR_EXPECT_JSON( json.is_object() ); 112 | return process_release( json, info ); 113 | } 114 | 115 | void process_error( const n_json& json, const char* http_status ) 116 | { 117 | if ( auto it = json.find( "message" ); it != json.end() && it->is_string() ) 118 | { 119 | throw exception_io_data( it.value().get().c_str() ); 120 | } 121 | else 122 | { 123 | throw exception_io_data( PFC_string_formatter() 124 | << "unexpected response; HTTP status: " << http_status ); 125 | } 126 | } 127 | 128 | private: 129 | const std::string componentRepo_; 130 | const std::string componentOwner_; 131 | }; 132 | 133 | } // namespace qwr::acfu 134 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/acfu/acfu_registration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "acfu_registration.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace 12 | { 13 | 14 | class AcfuSource 15 | : public ::acfu::source 16 | { 17 | public: 18 | AcfuSource( const GUID& guid, 19 | const std::string& componentName, 20 | const std::string& componentFilename, 21 | const std::string& componentRepo, 22 | const std::string& componentOwner ); 23 | 24 | public: 25 | GUID get_guid() override; 26 | void get_info( file_info& info ) override; 27 | bool is_newer( const file_info& info ) override; 28 | ::acfu::request::ptr create_request() override; 29 | 30 | private: 31 | std::string FetchVersion(); 32 | 33 | private: 34 | const GUID guid_; 35 | const std::string componentName_; 36 | const std::string componentFilename_; 37 | const std::string componentRepo_; 38 | const std::string componentOwner_; 39 | 40 | bool isVersionFetched_ = false; 41 | std::string installedVersion_; 42 | }; 43 | 44 | } // namespace 45 | 46 | namespace 47 | { 48 | 49 | AcfuSource::AcfuSource( const GUID& guid, 50 | const std::string& componentName, 51 | const std::string& componentFilename, 52 | const std::string& componentRepo, 53 | const std::string& componentOwner ) 54 | : guid_( guid ) 55 | , componentName_( componentName ) 56 | , componentFilename_( componentFilename ) 57 | , componentRepo_( componentRepo ) 58 | , componentOwner_( componentOwner ) 59 | { 60 | } 61 | 62 | GUID AcfuSource::get_guid() 63 | { 64 | return guid_; 65 | } 66 | 67 | void AcfuSource::get_info( file_info& info ) 68 | { 69 | if ( !isVersionFetched_ ) 70 | { 71 | installedVersion_ = FetchVersion(); 72 | isVersionFetched_ = true; 73 | } 74 | 75 | info.meta_set( "version", installedVersion_.c_str() ); 76 | info.meta_set( "name", componentName_.c_str() ); 77 | info.meta_set( "module", componentFilename_.c_str() ); 78 | } 79 | 80 | bool AcfuSource::is_newer( const file_info& info ) 81 | { 82 | if ( !info.meta_get( "version", 0 ) || installedVersion_.empty() ) 83 | { 84 | return false; 85 | } 86 | 87 | const auto availableVersion = [&info]() { 88 | std::string version = info.meta_get( "version", 0 ); 89 | if ( version[0] == 'v' ) 90 | { 91 | version = version.substr( 1 ); 92 | } 93 | return version; 94 | }(); 95 | 96 | try 97 | { 98 | return ( qwr::SemVer{ availableVersion } > qwr::SemVer{ installedVersion_ } ); 99 | } 100 | catch ( const std::runtime_error& ) 101 | { 102 | assert( false ); 103 | return false; 104 | } 105 | } 106 | 107 | std::string AcfuSource::FetchVersion() 108 | { 109 | auto cvRet = [&]() -> componentversion::ptr { 110 | for ( service_enum_t e; !e.finished(); ++e ) 111 | { 112 | auto cv = e.get(); 113 | 114 | pfc::string8 file_name; 115 | cv->get_file_name( file_name ); 116 | if ( file_name.equals( componentFilename_.c_str() ) ) 117 | { 118 | return cv; 119 | } 120 | } 121 | 122 | return componentversion::ptr{}; 123 | }(); 124 | 125 | if ( cvRet.is_empty() ) 126 | { 127 | return "0.0.0"; 128 | } 129 | else 130 | { 131 | pfc::string8 version; 132 | cvRet->get_component_version( version ); 133 | return std::string( version.c_str(), version.length() ); 134 | } 135 | } 136 | 137 | ::acfu::request::ptr AcfuSource::create_request() 138 | { 139 | return fb2k::service_new( componentRepo_, componentOwner_ ); 140 | } 141 | 142 | } // namespace 143 | 144 | namespace 145 | { 146 | 147 | std::vector>> g_registeredAcfu; 148 | 149 | } 150 | 151 | namespace Qwr::DotnetHost 152 | { 153 | 154 | void RegisterAcfu( Guid guid, String ^ componentName, String ^ componentFilename, String ^ componentRepo, String ^ repoOwner ) 155 | { 156 | g_registeredAcfu.emplace_back( std::make_unique>( 157 | Convert::ToNative::ToValue( guid ), 158 | Convert::ToNative::ToValue( componentName ), 159 | Convert::ToNative::ToValue( componentFilename ), 160 | Convert::ToNative::ToValue( componentRepo ), 161 | Convert::ToNative::ToValue( repoOwner ) ) ); 162 | } 163 | 164 | } // namespace Qwr::DotnetHost 165 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/acfu/acfu_registration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | void RegisterAcfu( Guid guid, String ^ componentName, String ^ componentFilename, String ^ componentRepo, String ^ repoOwner ); 9 | 10 | } // namespace Qwr::DotnetHost 11 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/acfu/semantic_version.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "semantic_version.h" 4 | 5 | #include 6 | 7 | namespace 8 | { 9 | 10 | std::string_view ExtractSuffixData( std::string_view& strView, char separator ) 11 | { 12 | std::string_view data; 13 | if ( size_t pos = strView.find_first_of( separator ); 14 | std::string::npos != pos ) 15 | { 16 | data = strView.substr( pos + 1 ); 17 | strView.remove_suffix( strView.size() - pos ); 18 | } 19 | return data; 20 | } 21 | 22 | template 23 | std::optional GetNumber( std::string_view strView, int base = 10 ) 24 | { 25 | T number; 26 | if ( auto [pos, ec] = std::from_chars( strView.data(), strView.data() + strView.size(), number, base ); 27 | ec == std::errc{} ) 28 | { 29 | return number; 30 | } 31 | else 32 | { 33 | return std::nullopt; 34 | } 35 | } 36 | 37 | std::vector SplitString( std::string_view input, char delim ) 38 | { 39 | std::vector result; 40 | size_t found = input.find( delim ); 41 | 42 | while ( found != std::string_view::npos ) 43 | { 44 | result.emplace_back( input.substr( 0, found + 1 ) ); 45 | input.remove_prefix( found + 1 ); 46 | found = input.find( delim ); 47 | } 48 | if ( input.empty() ) 49 | { 50 | result.emplace_back( input ); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | } // namespace 57 | 58 | namespace qwr 59 | { 60 | 61 | SemVer::SemVer( const std::string& strVer ) 62 | { 63 | const auto ret = ParseString( strVer ); 64 | if ( !ret ) 65 | { 66 | throw std::runtime_error( "Parsing failed" ); 67 | } 68 | *this = *ret; 69 | } 70 | 71 | std::optional SemVer::ParseString( const std::string& strVer ) 72 | { 73 | SemVer semVer; 74 | 75 | std::string_view curScope( strVer ); 76 | 77 | semVer.metadata = ExtractSuffixData( curScope, '+' ); 78 | semVer.prerelease = ExtractSuffixData( curScope, '-' ); 79 | 80 | if ( curScope.empty() ) 81 | { 82 | return std::nullopt; 83 | } 84 | 85 | std::vector> versionNums; 86 | for ( const auto& splitView: ::SplitString( curScope, '.' ) ) 87 | { 88 | const auto numRet = ::GetNumber( splitView ); 89 | if ( !numRet ) 90 | { 91 | return std::nullopt; 92 | } 93 | 94 | versionNums.emplace_back( numRet ); 95 | } 96 | 97 | if ( versionNums.empty() || versionNums.size() > 3 ) 98 | { 99 | return std::nullopt; 100 | } 101 | versionNums.resize( 3 ); 102 | 103 | semVer.major = *versionNums[0]; 104 | semVer.minor = versionNums[1].value_or( 0 ); 105 | semVer.patch = versionNums[2].value_or( 0 ); 106 | 107 | return semVer; 108 | } 109 | 110 | bool SemVer::operator==( const SemVer& other ) const 111 | { // metadata is ignored during comparison 112 | return ( major == other.major 113 | && minor == other.minor 114 | && patch == other.patch 115 | && prerelease == other.prerelease ); 116 | } 117 | bool SemVer::operator!=( const SemVer& other ) const 118 | { 119 | return ( !( *this == other ) ); 120 | } 121 | bool SemVer::operator<( const SemVer& other ) const 122 | { 123 | if ( major != other.major ) 124 | { 125 | return ( major < other.major ); 126 | } 127 | if ( minor != other.minor ) 128 | { 129 | return ( minor < other.minor ); 130 | } 131 | if ( patch != other.patch ) 132 | { 133 | return ( patch < other.patch ); 134 | } 135 | 136 | // metadata is ignored during comparison 137 | return IsPreleaseNewer( other.prerelease, prerelease ); 138 | } 139 | bool SemVer::operator>( const SemVer& other ) const 140 | { 141 | return ( other < *this ); 142 | } 143 | bool SemVer::operator<=( const SemVer& other ) const 144 | { 145 | return ( !( other < *this ) ); 146 | } 147 | bool SemVer::operator>=( const SemVer& other ) const 148 | { 149 | return ( !( *this < other ) ); 150 | } 151 | 152 | bool SemVer::IsPreleaseNewer( std::string_view a, std::string_view b ) 153 | { 154 | if ( a == b ) 155 | { 156 | return false; 157 | } 158 | 159 | if ( a.empty() || b.empty() ) 160 | { // Pre-release versions have a lower precedence than the associated normal version 161 | return a.empty(); 162 | } 163 | 164 | const auto isNumber = []( std::string_view str ) { 165 | return ( str.cend() == std::find_if_not( str.begin(), str.end(), []( char c ) { return std::isdigit( c ); } ) ); 166 | }; 167 | 168 | while ( !a.empty() && !b.empty() ) 169 | { 170 | const std::string_view a_Token = ExtractSuffixData( a, '.' ); 171 | const std::string_view b_Token = ExtractSuffixData( b, '.' ); 172 | 173 | if ( a_Token != b_Token ) 174 | { 175 | const bool a_isNumber = isNumber( a_Token ); 176 | const bool b_isNumber = isNumber( b_Token ); 177 | if ( a_isNumber != b_isNumber ) 178 | { // Numeric identifiers always have lower precedence than non-numeric identifiers 179 | return !a_isNumber; 180 | } 181 | else if ( a_isNumber && b_isNumber ) 182 | { 183 | auto numRet = ::GetNumber( a_Token ); 184 | assert( numRet ); // should be valid, because of `isNumber` check 185 | const int8_t aNum = *numRet; 186 | 187 | numRet = ::GetNumber( a_Token ); 188 | assert( numRet ); // should be valid, because of `isNumber` check 189 | const int8_t bNum = *numRet; 190 | 191 | assert( aNum != bNum ); // tokens would've been equal otherwise 192 | return aNum > bNum; 193 | } 194 | else 195 | { 196 | return a_Token > b_Token; 197 | } 198 | } 199 | else 200 | { 201 | if ( a.empty() != b.empty() ) 202 | { // A larger set of pre-release fields has a higher precedence than a smaller set 203 | return !a.empty(); 204 | } 205 | } 206 | } 207 | 208 | // They are equal (should not reach here) 209 | assert( 0 ); 210 | return false; 211 | } 212 | 213 | } // namespace qwr 214 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/acfu/semantic_version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace qwr 7 | { 8 | 9 | /// @brief See https://semver.org/ 10 | class SemVer 11 | { 12 | public: 13 | SemVer() = default; 14 | /// @throw std::runtime_error if parsing failed 15 | SemVer( const std::string& strVer ); 16 | 17 | static std::optional ParseString( const std::string& strVer ); 18 | 19 | bool operator==( const SemVer& other ) const; 20 | bool operator!=( const SemVer& other ) const; 21 | bool operator<( const SemVer& other ) const; 22 | bool operator>( const SemVer& other ) const; 23 | bool operator<=( const SemVer& other ) const; 24 | bool operator>=( const SemVer& other ) const; 25 | 26 | private: 27 | static bool IsPreleaseNewer( std::string_view a, std::string_view b ); 28 | 29 | public: 30 | uint8_t major = 0; 31 | uint8_t minor = 0; 32 | uint8_t patch = 0; 33 | std::string prerelease; 34 | std::string metadata; 35 | }; 36 | 37 | } // namespace qwr 38 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/component_defines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define DNET_NAME ".NET Component Host" 7 | #define DNET_UNDERSCORE_NAME "foo_dotnet_component_host" 8 | #define DNET_DLL_NAME DNET_UNDERSCORE_NAME ".dll" 9 | 10 | #define DNET_STRINGIFY_LITERAL( x ) #x 11 | #define DNET_STRINGIFY( x ) DNET_STRINGIFY_LITERAL( x ) 12 | 13 | #ifdef DNET_VERSION_PRERELEASE_TEXT 14 | # define DNET_VERSION_PRERELEASE "-" DNET_VERSION_PRERELEASE_TEXT 15 | # define DNET_VERSION_METADATA "+" DNET_STRINGIFY( DNET_COMMIT_HASH ) 16 | #else 17 | # define DNET_VERSION_PRERELEASE "" 18 | # define DNET_VERSION_METADATA "" 19 | #endif 20 | 21 | #ifdef _DEBUG 22 | # define DNET_VERSION_DEBUG_SUFFIX " (Debug)" 23 | #else 24 | # define DNET_VERSION_DEBUG_SUFFIX "" 25 | #endif 26 | 27 | #define DNET_VERSION \ 28 | DNET_STRINGIFY( DNET_VERSION_MAJOR ) \ 29 | "." DNET_STRINGIFY( DNET_VERSION_MINOR ) "." DNET_STRINGIFY( DNET_VERSION_PATCH ) \ 30 | DNET_VERSION_PRERELEASE DNET_VERSION_METADATA 31 | #define DNET_NAME_WITH_VERSION DNET_NAME " v" DNET_VERSION DNET_VERSION_DEBUG_SUFFIX 32 | 33 | #define DNET_ABOUT \ 34 | DNET_NAME_WITH_VERSION " by TheQwertiest\n" \ 35 | "Build: " __TIME__ ", " __DATE__ 36 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/component_guids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Qwr::DotnetHost::Guids 4 | { 5 | 6 | constexpr GUID ui_preferences = { 0x437b649b, 0xc572, 0x453b, { 0x87, 0x6, 0x5a, 0x7b, 0x33, 0xb, 0x3c, 0x84 } }; 7 | constexpr GUID acfu = { 0xe23c2ce, 0x9b32, 0x4b24, { 0x90, 0x1b, 0xc9, 0x9e, 0x6b, 0xb7, 0x1c, 0x7f } }; 8 | 9 | } // namespace Qwr::DotnetHost::Guids 10 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/component_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace System::Reflection; 4 | 5 | DECLARE_COMPONENT_VERSION( DNET_NAME, DNET_VERSION, DNET_ABOUT ); 6 | 7 | [assembly:AssemblyVersionAttribute( DNET_STRINGIFY( DNET_VERSION_MAJOR ) "." DNET_STRINGIFY( DNET_VERSION_MINOR ) ".*" )]; 8 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/component_paths.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "component_paths.h" 4 | 5 | namespace fs = std::filesystem; 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | #pragma unmanaged 11 | 12 | std::string ComponentDir() 13 | { 14 | static const auto dir = [] { 15 | pfc::string8_fast tmp; 16 | uGetModuleFileName( core_api::get_my_instance(), tmp ); 17 | 18 | return fs::u8path( tmp.c_str() ).parent_path().lexically_normal().u8string(); 19 | }(); 20 | 21 | return dir; 22 | } 23 | 24 | std::string Fb2kDir() 25 | { 26 | static const auto dir = [] { 27 | pfc::string8_fast tmp; 28 | uGetModuleFileName( nullptr, tmp ); 29 | 30 | return fs::u8path( tmp.c_str() ).parent_path().lexically_normal().u8string(); 31 | }(); 32 | 33 | return dir; 34 | } 35 | 36 | std::string ProfileDir() 37 | { 38 | static const auto dir = [] { 39 | if ( core_api::are_services_available() ) 40 | { 41 | std::string profile_path = core_api::get_profile_path(); 42 | if ( profile_path._Starts_with( "file://" ) ) 43 | { 44 | profile_path = profile_path.substr( sizeof( "file://" ) - 1 ); 45 | } 46 | 47 | return fs::u8path( profile_path ).lexically_normal().u8string(); 48 | } 49 | else 50 | { 51 | // can't use fb2k method to retrieve profile dir here, because it's not working yet. 52 | // hence we have to divine it: profile/user-components/current_module_dir/current_module.dll 53 | return fs::u8path( ComponentDir() ).parent_path().parent_path().lexically_normal().u8string(); 54 | } 55 | }(); 56 | 57 | return dir; 58 | } 59 | 60 | std::string ComponentWorkingDir() 61 | { 62 | static const auto dir = ( fs::u8path( ProfileDir() ) / DNET_UNDERSCORE_NAME ).u8string(); 63 | return dir; 64 | } 65 | 66 | std::string NetComponentsDir() 67 | { 68 | static const auto dir = ( fs::u8path( ComponentWorkingDir() ) / "components" ).u8string(); 69 | return dir; 70 | } 71 | 72 | std::string TempDir() 73 | { 74 | static const auto dir = ( fs::u8path( ComponentWorkingDir() ) / "tmp" ).u8string(); 75 | return dir; 76 | } 77 | 78 | std::string TempDir_ComponentsToInstall() 79 | { 80 | static const auto dir = ( fs::u8path( TempDir() ) / "components_to_install" ).u8string(); 81 | return dir; 82 | } 83 | 84 | std::string TempDir_ComponentsToRemove() 85 | { 86 | static const auto dir = ( fs::u8path( TempDir() ) / "components_to_remove" ).u8string(); 87 | return dir; 88 | } 89 | 90 | std::string TempDir_ComponentUnpack() 91 | { 92 | static const auto dir = ( fs::u8path( TempDir() ) / "unpacked_component" ).u8string(); 93 | return dir; 94 | } 95 | 96 | #pragma managed 97 | 98 | } // namespace Qwr::DotnetHost 99 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/component_paths.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | #pragma unmanaged 9 | 10 | std::string ComponentDir(); 11 | std::string Fb2kDir(); 12 | std::string ProfileDir(); 13 | std::string ComponentWorkingDir(); 14 | std::string NetComponentsDir(); 15 | std::string TempDir(); 16 | std::string TempDir_ComponentsToInstall(); 17 | std::string TempDir_ComponentsToRemove(); 18 | std::string TempDir_ComponentUnpack(); 19 | 20 | #pragma managed 21 | 22 | } // namespace Qwr::DotnetHost 23 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/convert/to_native.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "to_native.h" 4 | #include 5 | 6 | #include 7 | 8 | using namespace msclr::interop; 9 | 10 | namespace Qwr::DotnetHost::Convert::ToNative 11 | { 12 | 13 | std::string ToValue( String ^ inValue ) 14 | { 15 | if ( inValue == nullptr ) 16 | { 17 | throw gcnew ArgumentNullException(); 18 | } 19 | 20 | marshal_context ctx; 21 | const auto wstr = ctx.marshal_as( inValue ); 22 | 23 | size_t stringLen = WideCharToMultiByte( CP_UTF8, 24 | 0, 25 | wstr.data(), 26 | wstr.size(), 27 | nullptr, 28 | 0, 29 | nullptr, 30 | nullptr ); 31 | std::string outValue; 32 | outValue.resize( stringLen ); 33 | 34 | stringLen = WideCharToMultiByte( CP_UTF8, 35 | 0, 36 | wstr.data(), 37 | wstr.size(), 38 | outValue.data(), 39 | outValue.size(), 40 | nullptr, 41 | nullptr ); 42 | outValue.resize( stringLen ); 43 | 44 | return outValue; 45 | } 46 | 47 | GUID ToValue( Guid ^ inValue ) 48 | { 49 | if ( inValue == nullptr ) 50 | { 51 | throw gcnew ArgumentNullException(); 52 | } 53 | 54 | auto guidData = inValue->ToByteArray(); 55 | pin_ptr data = &( guidData[0] ); 56 | 57 | return *(_GUID*)data; 58 | } 59 | 60 | metadb_handle_ptr ToValue( IMetadbHandle ^ inValue ) 61 | { 62 | if ( inValue == nullptr ) 63 | { 64 | throw gcnew ArgumentNullException(); 65 | } 66 | 67 | auto concreteMetadb = dynamic_cast( inValue ); 68 | if ( concreteMetadb == nullptr ) 69 | { 70 | throw gcnew Exception( "Unexpected derived type of IMetadbHandle" ); 71 | } 72 | return concreteMetadb->Handle(); 73 | } 74 | 75 | metadb_handle_list ToValue( List ^ inValue ) 76 | { 77 | if ( inValue == nullptr ) 78 | { 79 | throw gcnew ArgumentNullException(); 80 | } 81 | 82 | metadb_handle_list handleList; 83 | for each ( auto netHandle in inValue ) 84 | { 85 | handleList.add_item( ToValue( netHandle ) ); 86 | } 87 | 88 | return handleList; 89 | } 90 | 91 | } // namespace Qwr::DotnetHost::Convert::ToStd 92 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/convert/to_native.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::Collections::Generic; 5 | using namespace Qwr::ComponentInterface; 6 | 7 | namespace Qwr::DotnetHost::Convert::ToNative 8 | { 9 | 10 | std::string ToValue( String ^ inValue ); 11 | GUID ToValue( Guid ^ inValue ); 12 | metadb_handle_ptr ToValue( IMetadbHandle ^ inValue ); 13 | metadb_handle_list ToValue( List ^ inValue ); 14 | 15 | } // namespace Qwr::DotnetHost::Convert::ToNative 16 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/convert/to_net.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "to_net.h" 4 | 5 | namespace Qwr::DotnetHost::Convert::ToNet 6 | { 7 | 8 | String ^ ToValue( std::string_view inValue ) { 9 | return gcnew String( inValue.data(), 0, inValue.size(), gcnew System::Text::UTF8Encoding( true, true ) ); 10 | } 11 | 12 | String 13 | ^ ToValue( const pfc::string_base& inValue ) { 14 | return ToValue( std::string_view{ inValue.c_str(), inValue.length() } ); 15 | } 16 | 17 | System::String 18 | ^ ToValue( const pfc::exception& inValue ) { 19 | return ToValue( std::string_view{ inValue.what() } ); 20 | } 21 | 22 | System::Guid 23 | ToValue( const GUID& inValue ) { 24 | return Guid( inValue.Data1, 25 | inValue.Data2, 26 | inValue.Data3, 27 | inValue.Data4[0], 28 | inValue.Data4[1], 29 | inValue.Data4[2], 30 | inValue.Data4[3], 31 | inValue.Data4[4], 32 | inValue.Data4[5], 33 | inValue.Data4[6], 34 | inValue.Data4[7] ); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/convert/to_net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using namespace System; 6 | 7 | namespace Qwr::DotnetHost::Convert::ToNet 8 | { 9 | 10 | String ^ ToValue( const char* inValue ) = delete; 11 | String ^ ToValue( std::string_view inValue ); 12 | String ^ ToValue( const pfc::string_base& inValue ); 13 | String ^ ToValue( const pfc::exception& inValue ); 14 | Guid ToValue( const GUID& inValue ); 15 | 16 | } // namespace Qwr::DotnetHost::Convert::ToNet 17 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/component_registration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "component_registration.h" 4 | 5 | #include 6 | 7 | namespace 8 | { 9 | 10 | class ComponentVersion : public componentversion 11 | { 12 | public: 13 | ComponentVersion( const std::string& filename, 14 | const std::string& name, 15 | const std::string& version, 16 | const std::string& description ) 17 | : filename_( filename ) 18 | , name_( name ) 19 | , version_( version ) 20 | , description_( description ) 21 | { 22 | } 23 | void get_file_name( pfc::string_base& out ) override 24 | { 25 | out.set_string( filename_.data(), filename_.size() ); 26 | } 27 | void get_component_name( pfc::string_base& out ) override 28 | { 29 | out.set_string( name_.data(), name_.size() ); 30 | } 31 | void get_component_version( pfc::string_base& out ) override 32 | { 33 | out.set_string( version_.data(), version_.size() ); 34 | } 35 | void get_about_message( pfc::string_base& out ) override 36 | { 37 | out.set_string( description_.data(), description_.size() ); 38 | } 39 | 40 | private: 41 | std::string filename_; 42 | std::string name_; 43 | std::string version_; 44 | std::string description_; 45 | }; 46 | 47 | } // namespace 48 | 49 | namespace 50 | { 51 | 52 | std::vector>> g_registeredComponent; 53 | 54 | } 55 | 56 | namespace Qwr::DotnetHost 57 | { 58 | 59 | void RegisterComponent( String ^ filename, ComponentInfo ^ componentInfo ) 60 | { 61 | g_registeredComponent.emplace_back( std::make_unique>( 62 | Convert::ToNative::ToValue( filename ), 63 | Convert::ToNative::ToValue( componentInfo->Name ) + " (.NET)", 64 | Convert::ToNative::ToValue( componentInfo->Version ), 65 | Convert::ToNative::ToValue( componentInfo->Description ) ) ); 66 | } 67 | 68 | } // namespace Qwr::DotnetHost 69 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/component_registration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | void RegisterComponent( String ^ filename, ComponentInfo ^ componentInfo ); 10 | 11 | } // namespace Qwr::DotnetHost 12 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/init_quit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace Qwr::DotnetHost; 6 | 7 | namespace 8 | { 9 | 10 | class InitQuit : public initquit 11 | { 12 | public: 13 | void on_init() override; 14 | void on_quit() override; 15 | }; 16 | 17 | } // namespace 18 | 19 | namespace 20 | { 21 | 22 | void InitQuit::on_init() 23 | { 24 | Host::GetInstance()->Start(); 25 | } 26 | 27 | void InitQuit::on_quit() 28 | { 29 | Host::GetInstance()->Shutdown(); 30 | } 31 | 32 | } 33 | 34 | namespace 35 | { 36 | 37 | FB2K_SERVICE_FACTORY( InitQuit ); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace 7 | { 8 | 9 | HINSTANCE g_hIns; 10 | 11 | pfc::string_simple g_name, g_full_path; 12 | 13 | bool g_services_available = false; 14 | bool g_initialized = false; 15 | 16 | } // namespace 17 | 18 | using namespace Qwr::DotnetHost; 19 | 20 | namespace core_api 21 | { 22 | 23 | 24 | HINSTANCE get_my_instance() 25 | { 26 | return g_hIns; 27 | } 28 | 29 | HWND get_main_window() 30 | { 31 | PFC_ASSERT( g_foobar2000_api ); 32 | return g_foobar2000_api->get_main_window(); 33 | } 34 | const char* get_my_file_name() 35 | { 36 | return g_name; 37 | } 38 | 39 | const char* get_my_full_path() 40 | { 41 | return g_full_path; 42 | } 43 | 44 | bool are_services_available() 45 | { 46 | return g_services_available; 47 | } 48 | bool assert_main_thread() 49 | { 50 | return ( g_services_available && g_foobar2000_api ? g_foobar2000_api->assert_main_thread() : true ); 51 | } 52 | 53 | void ensure_main_thread() 54 | { 55 | if ( !is_main_thread() ) 56 | { 57 | uBugCheck(); 58 | } 59 | } 60 | 61 | bool is_main_thread() 62 | { 63 | return ( g_services_available && g_foobar2000_api ? g_foobar2000_api->is_main_thread() : true ); 64 | } 65 | const char* get_profile_path() 66 | { 67 | PFC_ASSERT( g_foobar2000_api ); 68 | return g_foobar2000_api->get_profile_path(); 69 | } 70 | 71 | bool is_shutting_down() 72 | { 73 | return ( g_services_available && g_foobar2000_api ? g_foobar2000_api->is_shutting_down() : g_initialized ); 74 | } 75 | bool is_initializing() 76 | { 77 | return ( g_services_available && g_foobar2000_api ? g_foobar2000_api->is_initializing() : !g_initialized ); 78 | } 79 | bool is_portable_mode_enabled() 80 | { 81 | PFC_ASSERT( g_foobar2000_api ); 82 | return g_foobar2000_api->is_portable_mode_enabled(); 83 | } 84 | 85 | bool is_quiet_mode_enabled() 86 | { 87 | PFC_ASSERT( g_foobar2000_api ); 88 | return g_foobar2000_api->is_quiet_mode_enabled(); 89 | } 90 | } // namespace core_api 91 | 92 | namespace 93 | { 94 | class foobar2000_client_impl : public foobar2000_client 95 | , private foobar2000_component_globals 96 | { 97 | public: 98 | t_uint32 get_version() override 99 | { 100 | return FOOBAR2000_CLIENT_VERSION; 101 | } 102 | pservice_factory_base get_service_list() override 103 | { 104 | return service_factory_base::__internal__list; 105 | } 106 | 107 | void get_config( stream_writer* p_stream, abort_callback& p_abort ) override 108 | { 109 | cfg_var::config_write_file( p_stream, p_abort ); 110 | } 111 | 112 | void set_config( stream_reader* p_stream, abort_callback& p_abort ) override 113 | { 114 | cfg_var::config_read_file( p_stream, p_abort ); 115 | } 116 | 117 | void set_library_path( const char* path, const char* name ) override 118 | { 119 | g_full_path = path; 120 | g_name = name; 121 | } 122 | 123 | void services_init( bool val ) override 124 | { 125 | if ( val ) 126 | { 127 | g_initialized = true; 128 | } 129 | g_services_available = val; 130 | } 131 | 132 | bool is_debug() override 133 | { 134 | #ifdef _DEBUG 135 | return true; 136 | #else 137 | return false; 138 | #endif 139 | } 140 | }; 141 | 142 | } // namespace 143 | 144 | namespace 145 | { 146 | 147 | ::foobar2000_client_impl g_client; 148 | 149 | } 150 | 151 | extern "C" 152 | { 153 | __declspec( dllexport ) foobar2000_client* _cdecl foobar2000_get_interface( foobar2000_api* p_api, HINSTANCE hIns ) 154 | { 155 | g_hIns = hIns; 156 | g_foobar2000_api = p_api; 157 | 158 | TCHAR buf[MAX_PATH]; 159 | GetModuleFileName( hIns, buf, sizeof( buf ) ); 160 | auto modulePath = gcnew String( buf ); 161 | 162 | Host::GetInstance()->Initialize( modulePath ); 163 | 164 | return &g_client; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/main_menu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "main_menu.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace QwrConvert = Qwr::DotnetHost::Convert; 9 | 10 | namespace 11 | { 12 | 13 | using namespace Qwr::DotnetHost; 14 | 15 | class MainMenuCommands_Impl : public mainmenu_commands 16 | { 17 | public: 18 | MainMenuCommands_Impl( GUID parentGuid, uint32_t sortPriority ); 19 | 20 | t_uint32 get_command_count() override; 21 | GUID get_command( t_uint32 p_index ) override; 22 | void get_name( t_uint32 p_index, pfc::string_base& p_out ) override; 23 | bool get_description( t_uint32 p_index, pfc::string_base& p_out ) override; 24 | GUID get_parent() override; 25 | void execute( t_uint32 p_index, service_ptr_t p_callback ) override; 26 | bool get_display( t_uint32 p_index, pfc::string_base& p_out, t_uint32& p_flags ) override; 27 | 28 | void SetCommands( List ^ commands ); 29 | 30 | private: 31 | List ^ GetCommands(); 32 | 33 | private: 34 | const GUID parentGuid_; 35 | const uint32_t sortPriority_; 36 | 37 | gcroot ^> commands_; 38 | }; 39 | 40 | class MainMenuGroup_Impl : public mainmenu_group_popup_v2 41 | { 42 | public: 43 | MainMenuGroup_Impl( GUID guid, GUID parentGuid, const std::string& name, uint32_t sortPriority, bool isPopup ); 44 | 45 | GUID get_guid() override; 46 | GUID get_parent() override; 47 | t_uint32 get_sort_priority() override; 48 | void get_display_string( pfc::string_base& p_out ) override; 49 | bool popup_condition() override; 50 | 51 | private: 52 | const GUID guid_; 53 | const GUID parentGuid_; 54 | const std::string name_; 55 | const uint32_t sortPriority_; 56 | const bool isPopup_; 57 | }; 58 | 59 | } // namespace 60 | 61 | namespace 62 | { 63 | 64 | using namespace Qwr::DotnetHost; 65 | 66 | MainMenuCommands_Impl::MainMenuCommands_Impl( GUID parentGuid, uint32_t sortPriority ) 67 | : parentGuid_( parentGuid ) 68 | , sortPriority_( sortPriority ) 69 | { 70 | } 71 | 72 | t_uint32 MainMenuCommands_Impl::get_command_count() 73 | { 74 | return commands_->Count; 75 | } 76 | 77 | GUID MainMenuCommands_Impl::get_command( t_uint32 p_index ) 78 | { 79 | if ( p_index >= static_cast( commands_->Count ) ) 80 | { 81 | assert( false ); 82 | throw std::runtime_error( "Unexpected command index received" ); 83 | } 84 | 85 | return QwrConvert::ToNative::ToValue( GetCommands()[p_index]->GetGuid() ); 86 | } 87 | 88 | void MainMenuCommands_Impl::get_name( t_uint32 p_index, pfc::string_base& p_out ) 89 | { 90 | if ( p_index >= static_cast( commands_->Count ) ) 91 | { 92 | assert( false ); 93 | throw std::runtime_error( "Unexpected command index received" ); 94 | } 95 | 96 | auto name = QwrConvert::ToNative::ToValue( GetCommands()[p_index]->GetName() ); 97 | p_out.set_string( name.c_str(), name.size() ); 98 | } 99 | 100 | bool MainMenuCommands_Impl::get_description( t_uint32 p_index, pfc::string_base& p_out ) 101 | { 102 | if ( p_index >= static_cast( commands_->Count ) ) 103 | { 104 | assert( false ); 105 | throw std::runtime_error( "Unexpected command index received" ); 106 | } 107 | 108 | auto description = QwrConvert::ToNative::ToValue( GetCommands()[p_index]->GetDescription() ); 109 | if ( description.empty() ) 110 | { 111 | return false; 112 | } 113 | 114 | p_out.set_string( description.data(), description.size() ); 115 | return true; 116 | } 117 | 118 | GUID MainMenuCommands_Impl::get_parent() 119 | { 120 | return parentGuid_; 121 | } 122 | 123 | void MainMenuCommands_Impl::execute( t_uint32 p_index, service_ptr_t p_callback ) 124 | { 125 | if ( p_index >= static_cast( commands_->Count ) ) 126 | { 127 | assert( false ); 128 | throw std::runtime_error( "Unexpected command index received" ); 129 | } 130 | 131 | GetCommands()[p_index]->GetAction()(); 132 | } 133 | 134 | bool MainMenuCommands_Impl::get_display( t_uint32 p_index, pfc::string_base& p_out, t_uint32& p_flags ) 135 | { 136 | if ( p_index >= static_cast( commands_->Count ) ) 137 | { 138 | assert( false ); 139 | throw std::runtime_error( "Unexpected command index received" ); 140 | } 141 | 142 | auto command = GetCommands()[p_index]; 143 | auto name = QwrConvert::ToNative::ToValue( command->GetName() ); 144 | p_out.set_string( name.c_str(), name.size() ); 145 | 146 | if ( command->IsAlwaysHidden ) 147 | { 148 | return false; 149 | } 150 | 151 | p_flags = 0; 152 | if ( !command->IsEnabled ) 153 | { 154 | p_flags |= flag_disabled; 155 | } 156 | if ( command->IsChecked ) 157 | { 158 | p_flags |= flag_checked; 159 | } 160 | if ( command->IsRadioChecked ) 161 | { 162 | p_flags |= flag_radiochecked; 163 | } 164 | if ( command->IsDefaultHidden ) 165 | { 166 | p_flags |= flag_defaulthidden; 167 | } 168 | 169 | return true; 170 | } 171 | 172 | void MainMenuCommands_Impl::SetCommands( List ^ commands ) 173 | { 174 | assert( commands != nullptr ); 175 | commands_ = commands; 176 | } 177 | 178 | List ^ MainMenuCommands_Impl::GetCommands() 179 | { 180 | return commands_; 181 | } 182 | 183 | MainMenuGroup_Impl::MainMenuGroup_Impl( GUID guid, GUID parentGuid, const std::string& name, uint32_t sortPriority, bool isPopup ) 184 | : guid_( guid ) 185 | , parentGuid_( parentGuid ) 186 | , name_( name ) 187 | , sortPriority_( sortPriority ) 188 | , isPopup_( isPopup ) 189 | { 190 | } 191 | 192 | GUID MainMenuGroup_Impl::get_guid() 193 | { 194 | return guid_; 195 | } 196 | 197 | GUID MainMenuGroup_Impl::get_parent() 198 | { 199 | return parentGuid_; 200 | } 201 | 202 | t_uint32 MainMenuGroup_Impl::get_sort_priority() 203 | { 204 | return sortPriority_; 205 | } 206 | 207 | void MainMenuGroup_Impl::get_display_string( pfc::string_base& p_out ) 208 | { 209 | p_out.set_string( name_.c_str(), name_.size() ); 210 | } 211 | 212 | bool MainMenuGroup_Impl::popup_condition() 213 | { 214 | return isPopup_; 215 | } 216 | 217 | } // namespace 218 | 219 | namespace 220 | { 221 | 222 | std::vector>> g_mainMenuCommands; 223 | std::vector>> g_mainMenuGroups; 224 | 225 | } // namespace 226 | 227 | namespace Qwr::DotnetHost 228 | { 229 | 230 | void RegisterMainMenuCommandSection( GUID parentGuid, uint32_t sortPriority, List ^ commands ) 231 | { 232 | assert( commands != nullptr ); 233 | 234 | auto& pElem = g_mainMenuCommands.emplace_back( std::make_unique>( parentGuid, sortPriority ) ); 235 | pElem->get_static_instance().SetCommands( commands ); 236 | } 237 | 238 | void RegisterMainMenuGroup( GUID guid, GUID parentGuid, const std::string& name, uint32_t sortPriority, bool isPopup ) 239 | { 240 | g_mainMenuGroups.emplace_back( std::make_unique>( 241 | guid, parentGuid, name, sortPriority, isPopup ) ); 242 | } 243 | 244 | } // namespace Qwr::DotnetHost 245 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/main_menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System::Collections::Generic; 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | ref class NetFbMainMenuCommand; 9 | 10 | void RegisterMainMenuCommandSection( GUID parentGuid, uint32_t sortPriority, List ^ commands ); 11 | void RegisterMainMenuGroup( GUID guid, GUID parentGuid, const std::string& name, uint32_t sortPriority, bool isPopup ); 12 | 13 | } // namespace Qwr::DotnetHost 14 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/play_callback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "play_callback.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Qwr::DotnetHost 10 | { 11 | PlayCallbackImpl::PlayCallbackImpl( NetFbPlayCallback ^ parent ) 12 | : parent_( parent ) 13 | { 14 | assert( core_api::are_services_available() ); 15 | 16 | static_api_ptr_t pcm; 17 | pcm->register_callback( this, 18 | flag_on_playback_all | flag_on_volume_change, 19 | false ); 20 | } 21 | 22 | void PlayCallbackImpl::on_playback_new_track( metadb_handle_ptr p_track ) 23 | { 24 | parent_->OnPlaybackAdvancedToNewTrack( gcnew NetFbMetadbHandle( p_track ) ); 25 | } 26 | 27 | void PlayCallbackImpl::on_playback_time( double p_time ) 28 | { 29 | parent_->OnTrackPlaybackPositionChanged( p_time ); 30 | } 31 | 32 | void PlayCallbackImpl::on_playback_pause( bool p_state ) 33 | { 34 | parent_->OnPlaybackPausedStateChanged( p_state ); 35 | } 36 | 37 | void PlayCallbackImpl::on_playback_stop( play_control::t_stop_reason reason ) 38 | { 39 | parent_->OnPlaybackStopped( PlaybackStopReason( reason ) ); 40 | } 41 | 42 | void PlayCallbackImpl::on_playback_dynamic_info_track( const file_info& /*p_info*/ ) 43 | { 44 | parent_->OnDynamicPlaybackTrackInfoChanged(); 45 | } 46 | 47 | void PlayCallbackImpl::on_playback_starting( play_control::t_track_command p_command, bool p_paused ) 48 | { 49 | parent_->OnPlaybackStarting( gcnew PlaybackStartingEventArgs( PlaybackStartCommand( p_command ), p_paused ) ); 50 | } 51 | 52 | void PlayCallbackImpl::on_playback_seek( double p_time ) 53 | { 54 | parent_->OnTrackPlaybackPositionChanged( p_time ); 55 | } 56 | 57 | void PlayCallbackImpl::on_playback_edited( metadb_handle_ptr p_track ) 58 | { 59 | parent_->OnCurrentTrackInfoChanged( gcnew NetFbMetadbHandle( p_track ) ); 60 | } 61 | 62 | void PlayCallbackImpl::on_playback_dynamic_info( const file_info& /*p_info*/ ) 63 | { 64 | parent_->OnDynamicTrackInfoChanged(); 65 | } 66 | 67 | void PlayCallbackImpl::on_volume_change( float p_new_val ) 68 | { 69 | parent_->OnVolumeChanged( p_new_val ); 70 | } 71 | 72 | } // namespace Qwr::DotnetHost 73 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/play_callback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Qwr::DotnetHost 4 | { 5 | 6 | ref class NetFbPlayCallback; 7 | 8 | class PlayCallbackImpl : public play_callback 9 | { 10 | public: 11 | PlayCallbackImpl( NetFbPlayCallback ^ parent ); 12 | 13 | void on_playback_new_track( metadb_handle_ptr p_track ) override; 14 | void on_playback_time( double p_time ) override; 15 | void on_playback_stop( play_control::t_stop_reason reason ) override; 16 | void on_playback_pause( bool p_state ) override; 17 | void on_playback_dynamic_info_track( const file_info& p_info ) override; 18 | void on_playback_starting( play_control::t_track_command p_command, bool p_paused ) override; 19 | void on_playback_seek( double p_time ) override; 20 | void on_playback_edited( metadb_handle_ptr p_track ) override; 21 | void on_playback_dynamic_info( const file_info& p_info ) override; 22 | void on_volume_change( float p_new_val ) override; 23 | 24 | private: 25 | gcroot parent_; 26 | }; 27 | 28 | } // namespace Qwr::DotnetHost 29 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/preferences_pages.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "preferences_pages.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace 9 | { 10 | 11 | using namespace Qwr; 12 | 13 | class PreferencesPage : public preferences_page_v3 14 | { 15 | public: 16 | PreferencesPage( const std::string& name, 17 | const GUID& guid, 18 | const GUID& parentGuid, 19 | const std::string& helpUrl ); 20 | 21 | public: 22 | void SetPreferencesPageType( Type ^ preferencePageType ); 23 | 24 | preferences_page_instance::ptr instantiate( HWND parent, preferences_page_callback::ptr callback ) override; 25 | const char* get_name() override; 26 | bool get_help_url( pfc::string_base& p_out ) override; 27 | GUID get_guid() override; 28 | GUID get_parent_guid() override; 29 | 30 | private: 31 | const std::string name_; 32 | const GUID guid_; 33 | const GUID parentGuid_; 34 | const std::string helpUrl_; 35 | 36 | gcroot preferencePageType_; 37 | }; 38 | 39 | class PreferencesPageInstance : public preferences_page_instance 40 | { 41 | public: 42 | PreferencesPageInstance( preferences_page_callback::ptr callback ); 43 | ~PreferencesPageInstance(); 44 | 45 | public: 46 | void Initialize( HWND parent, Type ^ preferencePageType ); 47 | 48 | t_uint32 get_state() override; 49 | HWND get_wnd() override; 50 | void apply() override; 51 | void reset() override; 52 | 53 | protected: 54 | const preferences_page_callback::ptr callback_; 55 | gcroot netCallback_; 56 | gcroot pageImpl_; 57 | }; 58 | 59 | } // namespace 60 | 61 | namespace 62 | { 63 | 64 | using namespace Qwr::ComponentInterface; 65 | 66 | PreferencesPage::PreferencesPage( const std::string& name, const GUID& guid, const GUID& parentGuid, const std::string& helpUrl /*, Type ^ preferencePageType */ ) 67 | : name_( name ) 68 | , guid_( guid ) 69 | , parentGuid_( parentGuid ) 70 | , helpUrl_( helpUrl ) 71 | { 72 | } 73 | 74 | void PreferencesPage::SetPreferencesPageType( Type ^ preferencePageType ) 75 | { 76 | preferencePageType_ = preferencePageType; 77 | } 78 | 79 | preferences_page_instance::ptr PreferencesPage::instantiate( HWND parent, preferences_page_callback::ptr callback ) 80 | { 81 | auto pInstance = fb2k::service_new( callback ); 82 | pInstance->Initialize( parent, preferencePageType_ ); 83 | return pInstance; 84 | } 85 | 86 | const char* PreferencesPage::get_name() 87 | { 88 | return name_.c_str(); 89 | } 90 | 91 | bool PreferencesPage::get_help_url( pfc::string_base& p_out ) 92 | { 93 | if ( helpUrl_.empty() ) 94 | { 95 | return false; 96 | } 97 | 98 | p_out.set_string( helpUrl_.data(), helpUrl_.size() ); 99 | return true; 100 | } 101 | 102 | GUID PreferencesPage::get_guid() 103 | { 104 | return guid_; 105 | } 106 | 107 | GUID PreferencesPage::get_parent_guid() 108 | { 109 | return parentGuid_; 110 | } 111 | 112 | PreferencesPageInstance::PreferencesPageInstance( preferences_page_callback::ptr callback ) 113 | : callback_( callback ) 114 | { 115 | netCallback_ = gcnew DotnetHost::NetFbPreferencesPageCallback( callback ); 116 | } 117 | 118 | PreferencesPageInstance::~PreferencesPageInstance() 119 | { 120 | if ( !Object::ReferenceEquals( netCallback_, nullptr ) ) 121 | { 122 | delete netCallback_; 123 | } 124 | delete netCallback_; 125 | delete pageImpl_; 126 | } 127 | 128 | void PreferencesPageInstance::Initialize( HWND parent, Type ^ preferencePageType ) 129 | { 130 | try 131 | { 132 | auto ctorInfo = preferencePageType->GetConstructor( Type::EmptyTypes ); 133 | pageImpl_ = dynamic_cast( ctorInfo->Invoke( nullptr ) ); 134 | pageImpl_->Initialize( IntPtr( parent ), netCallback_ ); 135 | } 136 | catch ( Exception ^ e ) 137 | { 138 | if ( e == nullptr || e->Message == nullptr ) 139 | { 140 | throw std::runtime_error( "Preference page constructor failed: unknown error" ); 141 | } 142 | else 143 | { 144 | throw std::runtime_error( Qwr::DotnetHost::Convert::ToNative::ToValue( e->Message ) ); 145 | } 146 | } 147 | } 148 | 149 | t_uint32 PreferencesPageInstance::get_state() 150 | { 151 | return static_cast( pageImpl_->State() ); 152 | } 153 | HWND PreferencesPageInstance::get_wnd() 154 | { 155 | return (HWND)pageImpl_->Handle().ToPointer(); 156 | } 157 | void PreferencesPageInstance::apply() 158 | { 159 | pageImpl_->Apply(); 160 | } 161 | void PreferencesPageInstance::reset() 162 | { 163 | pageImpl_->Reset(); 164 | } 165 | 166 | } // namespace 167 | 168 | namespace 169 | { 170 | 171 | std::vector>> g_registeredPreferences; 172 | 173 | } 174 | 175 | namespace Qwr::DotnetHost 176 | { 177 | 178 | void RegisterPreferencesPage( PreferencesPageInfo ^ preferencesPageInfo, Type ^ preferencePageType ) 179 | { 180 | if ( !preferencePageType->IsAssignableTo( IPreferencesPage::typeid ) ) 181 | { 182 | throw gcnew ArgumentException( "Not compatible type", "preferencePageType" ); 183 | } 184 | 185 | auto& pElem = g_registeredPreferences.emplace_back( std::make_unique>( 186 | Convert::ToNative::ToValue( preferencesPageInfo->Name ), 187 | Convert::ToNative::ToValue( preferencesPageInfo->Guid ), 188 | Convert::ToNative::ToValue( preferencesPageInfo->ParentGuid ), 189 | Convert::ToNative::ToValue( preferencesPageInfo->HelpUrl == nullptr ? gcnew String( "" ) : preferencesPageInfo->HelpUrl ) ) ); 190 | pElem->get_static_instance().SetPreferencesPageType( preferencePageType ); 191 | }; 192 | 193 | } // namespace Qwr::DotnetHost 194 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/fb2k/preferences_pages.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | void RegisterPreferencesPage( PreferencesPageInfo ^ preferencesPageInfo, Type ^ preferencePageType ); 10 | 11 | }; 12 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/foo_dotnet_component_host.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {cbfc46a7-99a5-43d9-b414-62b137af537e} 18 | 19 | 20 | {1837df39-0b14-4668-a651-cc5d334af251} 21 | 22 | 23 | {c891dde2-ed24-4f57-a20c-eef0623a95ef} 24 | 25 | 26 | {63cfbdd9-befd-4478-923c-d77241d0bc5a} 27 | 28 | 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files\convert 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files\fb2k 41 | 42 | 43 | Source Files\loader 44 | 45 | 46 | Source Files\loader 47 | 48 | 49 | Source Files\host 50 | 51 | 52 | Source Files\host 53 | 54 | 55 | Source Files\host 56 | 57 | 58 | Source Files\fb2k 59 | 60 | 61 | Source Files\convert 62 | 63 | 64 | Source Files\fb2k 65 | 66 | 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files\convert 76 | 77 | 78 | Source Files\fb2k 79 | 80 | 81 | Source Files\loader 82 | 83 | 84 | Source Files\loader 85 | 86 | 87 | Source Files\loader 88 | 89 | 90 | Source Files\fb2k 91 | 92 | 93 | Source Files\host 94 | 95 | 96 | Source Files\host 97 | 98 | 99 | Source Files\fb2k 100 | 101 | 102 | Source Files\fb2k 103 | 104 | 105 | Source Files\host 106 | 107 | 108 | Source Files\fb2k 109 | 110 | 111 | Source Files\convert 112 | 113 | 114 | Source Files\fb2k 115 | 116 | 117 | 118 | 119 | Source Files 120 | 121 | 122 | Source Files\fb2k 123 | 124 | 125 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | ref struct Component 10 | { 11 | String ^ fullPath = nullptr; 12 | String ^ underscoredName = nullptr; 13 | ComponentInfo ^ info = nullptr; 14 | Qwr::ComponentInterface::IComponent ^ instance = nullptr; 15 | }; 16 | 17 | } // namespace Qwr::DotnetHost 18 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/delayed_installation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "delayed_installation.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace System::IO; 10 | 11 | namespace 12 | { 13 | 14 | void ClearDelayedStatus( String ^ componentName ) 15 | { 16 | auto toRemoveFile = Path::Combine( Qwr::DotnetHost::Convert::ToNet::ToValue( Qwr::DotnetHost::TempDir_ComponentsToRemove() ), componentName ); 17 | auto toInstallDir = Path::Combine( Qwr::DotnetHost::Convert::ToNet::ToValue( Qwr::DotnetHost::TempDir_ComponentsToInstall() ), componentName ); 18 | 19 | if ( File::Exists( toRemoveFile ) ) 20 | { 21 | File::Delete( toRemoveFile ); 22 | } 23 | if ( Directory::Exists( toInstallDir ) ) 24 | { 25 | Directory::Delete( toInstallDir, true ); 26 | } 27 | } 28 | 29 | } // namespace 30 | 31 | namespace Qwr::DotnetHost 32 | { 33 | 34 | ComponentDelayedStatus GetComponentDelayedStatus( String ^ componentName ) 35 | { 36 | auto toRemoveFile = Path::Combine( Convert::ToNet::ToValue( TempDir_ComponentsToRemove() ), componentName ); 37 | auto toInstallDir = Path::Combine( Convert::ToNet::ToValue( TempDir_ComponentsToInstall() ), componentName ); 38 | 39 | if ( File::Exists( toRemoveFile ) ) 40 | { 41 | return ComponentDelayedStatus::ToBeRemoved; 42 | } 43 | else if ( Directory::Exists( toInstallDir ) ) 44 | { 45 | return ComponentDelayedStatus::ToBeUpdated; 46 | } 47 | else 48 | { 49 | return ComponentDelayedStatus::NotDelayed; 50 | } 51 | } 52 | 53 | void ClearAllComponentDelayedStatuses() 54 | { 55 | auto toRemoveDir = Convert::ToNet::ToValue( TempDir_ComponentsToRemove() ); 56 | auto toInstallDir = Convert::ToNet::ToValue( TempDir_ComponentsToInstall() ); 57 | 58 | if ( Directory::Exists( toRemoveDir ) ) 59 | { 60 | Directory::Delete( toRemoveDir, true ); 61 | } 62 | if ( Directory::Exists( toInstallDir ) ) 63 | { 64 | Directory::Delete( toInstallDir, true ); 65 | } 66 | } 67 | 68 | void MarkComponentAsToBeRemoved( String ^ componentName ) 69 | { 70 | ClearDelayedStatus( componentName ); 71 | 72 | auto toRemoveFile = Path::Combine( Convert::ToNet::ToValue( TempDir_ComponentsToRemove() ), componentName ); 73 | Directory::CreateDirectory( Path::GetDirectoryName( toRemoveFile ) ); 74 | File::Create( toRemoveFile ); 75 | } 76 | 77 | void MarkComponentAsToBeInstalled( String ^ componentName, String ^ componentContentDir ) 78 | { 79 | ClearDelayedStatus( componentName ); 80 | 81 | auto toInstallDir = Path::Combine( Convert::ToNet::ToValue( TempDir_ComponentsToInstall() ), componentName ); 82 | Directory::CreateDirectory( Path::GetDirectoryName( toInstallDir ) ); 83 | Directory::Move( componentContentDir, toInstallDir ); 84 | } 85 | 86 | void ProcessDelayedComponents() 87 | { 88 | try 89 | { 90 | auto toRemoveDir = Convert::ToNet::ToValue( TempDir_ComponentsToRemove() ); 91 | auto toInstallDir = Convert::ToNet::ToValue( TempDir_ComponentsToInstall() ); 92 | 93 | if ( Directory::Exists( toRemoveDir ) ) 94 | { 95 | for each ( auto f in Directory::EnumerateFiles( toRemoveDir ) ) 96 | { 97 | auto componentDir = Path::Combine( Convert::ToNet::ToValue( NetComponentsDir() ), Path::GetFileNameWithoutExtension( f ) ); 98 | Directory::Delete( componentDir, true ); 99 | } 100 | } 101 | if ( Directory::Exists( toInstallDir ) ) 102 | { 103 | for each ( auto f in Directory::EnumerateDirectories( toInstallDir ) ) 104 | { 105 | auto componentDir = Path::Combine( Convert::ToNet::ToValue( NetComponentsDir() ), Path::GetFileNameWithoutExtension( f ) ); 106 | if ( Directory::Exists( componentDir ) ) 107 | { 108 | Directory::Delete( componentDir, true ); 109 | } 110 | Directory::CreateDirectory( Path::GetDirectoryName( componentDir ) ); 111 | Directory::Move( f, componentDir ); 112 | } 113 | } 114 | 115 | ClearAllComponentDelayedStatuses(); 116 | } 117 | catch ( Exception ^ /*e*/ ) 118 | { 119 | ClearAllComponentDelayedStatuses(); 120 | throw; 121 | } 122 | } 123 | 124 | } // namespace Qwr::DotnetHost 125 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/delayed_installation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | enum class ComponentDelayedStatus 9 | { 10 | ToBeRemoved, 11 | ToBeUpdated, 12 | NotDelayed 13 | }; 14 | 15 | ComponentDelayedStatus GetComponentDelayedStatus( String ^ componentName ); 16 | void ClearAllComponentDelayedStatuses(); 17 | 18 | void MarkComponentAsToBeRemoved( String ^ componentName ); 19 | void MarkComponentAsToBeInstalled( String ^ componentName, String ^ componentContentDir ); 20 | 21 | void ProcessDelayedComponents(); 22 | 23 | } // namespace Qwr::DotnetHost 24 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/fb2k_controls.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb2k_controls.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Qwr::DotnetHost 12 | { 13 | 14 | Fb2kControls::Fb2kControls() 15 | : pConsole_( gcnew Qwr::DotnetHost::NetFbConsole() ) 16 | , pPlaybackControls_( gcnew Qwr::DotnetHost::NetFbPlayControl() ) 17 | { 18 | } 19 | 20 | IConsole ^ Fb2kControls::Console() 21 | { 22 | return pConsole_; 23 | } 24 | 25 | void Fb2kControls::ExecuteContextMenuCommand( String ^ command, List ^ metadbHandles ) 26 | { 27 | metadb_handle_list handleList; 28 | if ( metadbHandles ) 29 | { 30 | handleList = Convert::ToNative::ToValue( metadbHandles ); 31 | } 32 | 33 | Qwr::DotnetHost::ExecuteContextMenuCommand( Convert::ToNative::ToValue( command ), handleList, contextmenu_manager::flag_view_full ); 34 | } 35 | 36 | void Fb2kControls::ExecuteMainMenuCommand( String ^ command ) 37 | { 38 | Qwr::DotnetHost::ExecuteMainMenuCommand( Convert::ToNative::ToValue( command ) ); 39 | } 40 | 41 | IPlaybackControls ^ Fb2kControls::PlaybackControls() 42 | { 43 | return pPlaybackControls_; 44 | } 45 | 46 | ITitleFormat ^ Fb2kControls::TitleFormat( String ^ expression ) 47 | { 48 | return gcnew NetFbTitleFormat( expression ); 49 | } 50 | 51 | } // namespace Qwr::DotnetHost 52 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/fb2k_controls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::Collections::Generic; 5 | using namespace Qwr::ComponentInterface; 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | private 11 | ref class Fb2kControls sealed : public IControls 12 | { 13 | public: 14 | Fb2kControls(); 15 | 16 | public: 17 | virtual IConsole ^ Console(); 18 | virtual void ExecuteContextMenuCommand( String ^ command, List ^ metadbHandles ); 19 | virtual void ExecuteMainMenuCommand( String ^ command ); 20 | virtual IPlaybackControls ^ PlaybackControls(); 21 | virtual ITitleFormat ^ TitleFormat( String ^ expression ); 22 | 23 | private: 24 | IConsole ^ pConsole_ = nullptr; 25 | IPlaybackControls ^ pPlaybackControls_ = nullptr; 26 | }; 27 | 28 | } // namespace Qwr::DotnetHost 29 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/fb2k_services.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb2k_services.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace Qwr::DotnetHost 13 | { 14 | 15 | void Fb2kStaticServices::RegisterPreferencesPage( PreferencesPageInfo preferencesPageInfo, Type ^ preferencePageType ) 16 | { 17 | if ( Host::GetInstance()->IsInitialized() ) 18 | { 19 | throw gcnew Exception( "Static services can't be initialized after the component start" ); 20 | } 21 | Qwr::DotnetHost::RegisterPreferencesPage( preferencesPageInfo, preferencePageType ); 22 | } 23 | 24 | generic IConfigVar ^ Fb2kStaticServices::RegisterConfigVar( Guid cfgGuid, T defaultValue ) 25 | { 26 | if ( Host::GetInstance()->IsInitialized() ) 27 | { 28 | throw gcnew Exception( "Static services can't be initialized after the component start" ); 29 | } 30 | return gcnew NetFbConfigVar( cfgGuid, defaultValue ); 31 | } 32 | 33 | IMainMenuGroup ^ Fb2kStaticServices::GetMainMenuGroup( Guid mainMenuGroupGuid ) 34 | { 35 | return gcnew NetFbMainMenuGroup( mainMenuGroupGuid ); 36 | } 37 | 38 | void Fb2kStaticServices::RegisterAcfu( Guid guid, String ^ componentRepo, String ^ repoOwner ) 39 | { 40 | if ( Host::GetInstance()->IsInitialized() ) 41 | { 42 | throw gcnew Exception( "Static services can't be initialized after the component start" ); 43 | } 44 | 45 | auto currentComponent = Host::GetInstance()->GetCurrentComponent(); 46 | assert( currentComponent ); 47 | 48 | Qwr::DotnetHost::RegisterAcfu( guid, currentComponent->info->Name, currentComponent->underscoredName, componentRepo, repoOwner ); 49 | } 50 | 51 | IPlaybackCallbacks ^ Fb2kDynamicServices::RegisterForPlaybackCallbacks() 52 | { 53 | if ( !pPlaybackCallbacks_ ) 54 | { 55 | pPlaybackCallbacks_ = gcnew NetFbPlayCallback(); 56 | } 57 | 58 | return pPlaybackCallbacks_; 59 | } 60 | 61 | } // namespace Qwr::DotnetHost 62 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/fb2k_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | private 10 | ref class Fb2kStaticServices sealed : public IStaticServicesManager 11 | { 12 | public: 13 | virtual void RegisterPreferencesPage( PreferencesPageInfo preferencesPageInfo, Type ^ preferencePageType ); 14 | 15 | generic virtual IConfigVar ^ RegisterConfigVar( Guid cfgGuid, T defaultValue ); 16 | 17 | virtual IMainMenuGroup ^ GetMainMenuGroup( Guid mainMenuGroupGuid ); 18 | 19 | virtual void RegisterAcfu( Guid guid, String ^ componentRepo, String ^ repoOwner ); 20 | }; 21 | 22 | private 23 | ref class Fb2kDynamicServices sealed : public IDynamicServicesManager 24 | { 25 | public: 26 | virtual IPlaybackCallbacks ^ RegisterForPlaybackCallbacks(); 27 | 28 | private: 29 | IPlaybackCallbacks ^ pPlaybackCallbacks_ = nullptr; 30 | }; 31 | 32 | } // namespace Qwr::DotnetHost 33 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/fb2k_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb2k_utils.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | using namespace System::Reflection; 12 | using namespace System::IO; 13 | 14 | namespace Qwr::DotnetHost 15 | { 16 | 17 | Fb2kUtils::Fb2kUtils() 18 | { 19 | } 20 | 21 | Fb2kUtils::~Fb2kUtils() 22 | { 23 | this->!Fb2kUtils(); 24 | } 25 | 26 | Fb2kUtils::!Fb2kUtils() 27 | { 28 | if ( pUiControl_ ) 29 | { 30 | delete pUiControl_; 31 | pUiControl_ = nullptr; 32 | } 33 | } 34 | 35 | void Fb2kUtils::Initialize() 36 | { 37 | assert( Host::GetInstance()->IsInitialized() ); 38 | pUiControl_ = new static_api_ptr_t(); 39 | } 40 | 41 | Version ^ Fb2kUtils::HostVersion() 42 | { 43 | return Assembly::GetExecutingAssembly()->GetName()->Version; 44 | } 45 | 46 | Guid Fb2kUtils::Fb2kGuid( Fb2kGuidId id ) 47 | { 48 | switch ( id ) 49 | { 50 | case Fb2kGuidId::PrefPage_Tagging: 51 | return Convert::ToNet::ToValue( preferences_page::guid_tagging ); 52 | case Fb2kGuidId::PrefPage_Root: 53 | return Convert::ToNet::ToValue( preferences_page::guid_root ); 54 | case Fb2kGuidId::PrefPage_Tools: 55 | return Convert::ToNet::ToValue( preferences_page::guid_tools ); 56 | case Fb2kGuidId::PrefPage_Display: 57 | return Convert::ToNet::ToValue( preferences_page::guid_display ); 58 | case Fb2kGuidId::PrefPage_Playback: 59 | return Convert::ToNet::ToValue( preferences_page::guid_playback ); 60 | case Fb2kGuidId::PrefPage_Visualizations: 61 | return Convert::ToNet::ToValue( preferences_page::guid_visualisations ); 62 | case Fb2kGuidId::PrefPage_Input: 63 | return Convert::ToNet::ToValue( preferences_page::guid_input ); 64 | case Fb2kGuidId::PrefPage_Core: 65 | return Convert::ToNet::ToValue( preferences_page::guid_core ); 66 | case Fb2kGuidId::PrefPage_TagWriting: 67 | return Convert::ToNet::ToValue( preferences_page::guid_tag_writing ); 68 | case Fb2kGuidId::PrefPage_MediaLibrary: 69 | return Convert::ToNet::ToValue( preferences_page::guid_media_library ); 70 | case Fb2kGuidId::PrefPage_Output: 71 | return Convert::ToNet::ToValue( preferences_page::guid_output ); 72 | case Fb2kGuidId::PrefPage_Advanced: 73 | return Convert::ToNet::ToValue( preferences_page::guid_advanced ); 74 | case Fb2kGuidId::PrefPage_Components: 75 | return Convert::ToNet::ToValue( preferences_page::guid_components ); 76 | case Fb2kGuidId::MainMenuGroups_File: 77 | return Convert::ToNet::ToValue( mainmenu_groups::file ); 78 | case Fb2kGuidId::MainMenuGroups_Edit: 79 | return Convert::ToNet::ToValue( mainmenu_groups::edit ); 80 | case Fb2kGuidId::MainMenuGroups_View: 81 | return Convert::ToNet::ToValue( mainmenu_groups::view ); 82 | case Fb2kGuidId::MainMenuGroups_Playback: 83 | return Convert::ToNet::ToValue( mainmenu_groups::playback ); 84 | case Fb2kGuidId::MainMenuGroups_Library: 85 | return Convert::ToNet::ToValue( mainmenu_groups::library ); 86 | case Fb2kGuidId::MainMenuGroups_Help: 87 | return Convert::ToNet::ToValue( mainmenu_groups::help ); 88 | default: 89 | throw gcnew ArgumentOutOfRangeException(); 90 | } 91 | } 92 | 93 | Drawing::Icon ^ Fb2kUtils::Fb2kIcon() 94 | { 95 | if ( !Host::GetInstance()->IsInitialized() ) 96 | { 97 | throw gcnew Exception( "This method can be called only after component initialization" ); 98 | } 99 | 100 | HICON hIcon = ( *pUiControl_ ).get_ptr()->get_main_icon(); 101 | return Drawing::Icon::FromHandle( (IntPtr)hIcon ); 102 | } 103 | 104 | String ^ Fb2kUtils::Fb2kPath() 105 | { 106 | return Convert::ToNet::ToValue( Qwr::DotnetHost::Fb2kDir() ) + Path::DirectorySeparatorChar; 107 | } 108 | 109 | String ^ Fb2kUtils::Fb2kVersion() 110 | { 111 | if ( !Host::GetInstance()->IsInitialized() ) 112 | { 113 | throw gcnew Exception( "This method can be called only after component initialization" ); 114 | } 115 | 116 | const auto pChar = core_version_info_v2::get()->get_version_as_text(); 117 | assert( pChar ); 118 | 119 | return Convert::ToNet::ToValue( std::string_view( pChar ) ); 120 | } 121 | 122 | bool Fb2kUtils::IsFb2kMinimized() 123 | { 124 | if ( !pUiControl_ ) 125 | { 126 | throw gcnew Exception( "This method can be called only after component initialization" ); 127 | } 128 | 129 | return !( *pUiControl_ ).get_ptr()->is_visible(); 130 | } 131 | 132 | String ^ Fb2kUtils::ProfilePath() 133 | { 134 | return Convert::ToNet::ToValue( Qwr::DotnetHost::ProfileDir() ) + Path::DirectorySeparatorChar; 135 | } 136 | 137 | void Fb2kUtils::ShowPopup( String ^ text, String ^ title ) 138 | { 139 | popup_message::g_show( Convert::ToNative::ToValue( text ).c_str(), Convert::ToNative::ToValue( title ).c_str() ); 140 | } 141 | 142 | } // namespace Qwr::DotnetHost 143 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/fb2k_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | private 10 | ref class Fb2kUtils sealed : public IUtils 11 | { 12 | public: 13 | Fb2kUtils(); 14 | ~Fb2kUtils(); 15 | !Fb2kUtils(); 16 | 17 | void Initialize(); 18 | 19 | virtual Version ^ HostVersion(); 20 | virtual Guid Fb2kGuid( Fb2kGuidId id ); 21 | virtual Drawing::Icon ^ Fb2kIcon(); 22 | virtual String ^ Fb2kPath(); 23 | virtual String ^ Fb2kVersion(); 24 | virtual bool IsFb2kMinimized(); 25 | virtual String ^ ProfilePath(); 26 | virtual void ShowPopup( String ^ text, String ^ title ); 27 | 28 | private: 29 | static_api_ptr_t* pUiControl_ = nullptr; 30 | }; 31 | 32 | } // namespace Qwr::DotnetHost 33 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/host.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "host.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | using namespace System::IO; 21 | 22 | // TODO: replace WinForms with System.Drawing.Common 23 | 24 | namespace Qwr::DotnetHost 25 | { 26 | 27 | Host::Host() 28 | { 29 | } 30 | 31 | Host ^ Host::GetInstance() 32 | { 33 | if ( self_ == nullptr ) 34 | { 35 | self_ = gcnew Host(); 36 | } 37 | return self_; 38 | } 39 | 40 | void Host::Initialize( String ^ modulePath ) 41 | { 42 | modulePath_ = modulePath; 43 | 44 | try 45 | { 46 | System::Windows::Forms::Application::SetCompatibleTextRenderingDefault( false ); 47 | 48 | fb2kControls_ = gcnew Fb2kControls(); 49 | fb2kStaticServices_ = gcnew Fb2kStaticServices(); 50 | fb2kDynamicServices_ = gcnew Fb2kDynamicServices(); 51 | fb2kUtils_ = gcnew Fb2kUtils(); 52 | components_ = gcnew List; 53 | 54 | Qwr::DotnetHost::RegisterPreferencesPage( Preferences::GetInfo(), Preferences::typeid ); 55 | Qwr::DotnetHost::RegisterAcfu( Convert::ToNet::ToValue( Guids::acfu ), DNET_NAME, DNET_UNDERSCORE_NAME, DNET_UNDERSCORE_NAME, "TheQwertiest" ); 56 | 57 | ProcessDelayedComponents(); 58 | 59 | auto componentLoader = gcnew ComponentLoader(); 60 | 61 | pfc::hires_timer timer; 62 | timer.start(); 63 | 64 | auto components = componentLoader->GetComponentsInDir( Convert::ToNet::ToValue( NetComponentsDir() ) ); 65 | for each ( auto component in components ) 66 | { 67 | try 68 | { 69 | componentLoader->LoadComponent( component ); 70 | component->info = component->instance->GetInfo(); 71 | RegisterComponent( Path::GetFileNameWithoutExtension( component->underscoredName ), component->info ); 72 | 73 | currentComponent_ = component; 74 | component->instance->Initialize( fb2kStaticServices_, fb2kUtils_ ); 75 | 76 | components_->Add( component ); 77 | } 78 | catch ( Exception ^ e ) 79 | { 80 | auto msg = gcnew String( "Error loading .NET component:\n" ); 81 | msg += " component: " + component->underscoredName + "\n"; 82 | msg += " error: " + e->Message; 83 | DelayedLog( msg ); 84 | } 85 | 86 | currentComponent_ = nullptr; 87 | DelayedLog( gcnew String( "Loaded `" + component->underscoredName + "`" ), true, false ); 88 | } 89 | 90 | DelayedLog( gcnew String( ( DNET_NAME_WITH_VERSION ": components loaded in " + std::to_string( static_cast( timer.query() * 1000 ) ) + "ms" ).c_str() ), false, false ); 91 | } 92 | catch ( Exception ^ e ) 93 | { 94 | DelayedLog( e->Message ); 95 | } 96 | 97 | isInitialized_ = true; 98 | } 99 | 100 | void Host::Start() 101 | { 102 | DrainDelayedLog(); 103 | 104 | fb2kUtils_->Initialize(); 105 | 106 | pfc::hires_timer timer; 107 | timer.start(); 108 | 109 | for each ( auto component in components_ ) 110 | { 111 | try 112 | { 113 | component->instance->Start( fb2kDynamicServices_, fb2kControls_ ); 114 | } 115 | catch ( Exception ^ e ) 116 | { 117 | auto msg = gcnew String( "Error during .NET component startup:\n" ); 118 | msg += " component: " + component->underscoredName + "\n"; 119 | msg += " error: " + e->Message; 120 | DelayedLog( msg ); 121 | } 122 | } 123 | 124 | DelayedLog( gcnew String( ( DNET_NAME_WITH_VERSION ": components initialized in " + std::to_string( static_cast( timer.query() * 1000 ) ) + "ms" ).c_str() ), false, false ); 125 | } 126 | 127 | void Host::Shutdown() 128 | { 129 | for each ( auto component in components_ ) 130 | { 131 | component->instance->Shutdown(); 132 | delete component->instance; 133 | } 134 | 135 | delete components_; 136 | delete fb2kDynamicServices_; 137 | delete fb2kControls_; 138 | } 139 | 140 | bool Host::IsInitialized() 141 | { 142 | return isInitialized_; 143 | } 144 | 145 | List ^ Host::GetComponents() 146 | { 147 | return components_; 148 | } 149 | 150 | Component ^ Host::GetCurrentComponent() 151 | { 152 | return currentComponent_; 153 | } 154 | 155 | } // namespace Qwr::DotnetHost 156 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/host/host.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using namespace System; 6 | using namespace System::Collections::Generic; 7 | using namespace Qwr::ComponentInterface; 8 | 9 | namespace Qwr::DotnetHost 10 | { 11 | 12 | ref class Fb2kControls; 13 | ref class Fb2kStaticServices; 14 | ref class Fb2kDynamicServices; 15 | ref class Fb2kUtils; 16 | 17 | private 18 | ref class Host 19 | { 20 | public: 21 | Host(); 22 | 23 | static Host ^ GetInstance(); 24 | 25 | public: 26 | void Initialize( String ^ modulePath ); 27 | void Start(); 28 | void Shutdown(); 29 | 30 | bool IsInitialized(); 31 | 32 | List ^ GetComponents(); 33 | 34 | // TODO: remove the need for this 35 | Component ^ GetCurrentComponent(); 36 | 37 | private: 38 | static Host ^ self_; 39 | 40 | bool isInitialized_ = false; 41 | bool hasFailed_ = false; 42 | 43 | Component ^ currentComponent_ = nullptr; 44 | 45 | List ^ components_ = nullptr; 46 | Fb2kControls ^ fb2kControls_ = nullptr; 47 | Fb2kStaticServices ^ fb2kStaticServices_ = nullptr; 48 | Fb2kDynamicServices ^ fb2kDynamicServices_ = nullptr; 49 | Fb2kUtils ^ fb2kUtils_ = nullptr; 50 | 51 | String ^ modulePath_ = nullptr; 52 | }; 53 | 54 | } // namespace Qwr::DotnetHost 55 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/loader/assembly_loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "assembly_loader.h" 4 | 5 | using namespace System::Reflection; 6 | using namespace System; 7 | using namespace System::IO; 8 | 9 | namespace 10 | { 11 | 12 | static Assembly ^ LoadFromSameFolder( Object ^ sender, ResolveEventArgs ^ args ) { 13 | auto folderPath = 14 | Path::GetDirectoryName( Assembly::GetExecutingAssembly()->Location ); 15 | auto assemblyPath = 16 | Path::Combine( folderPath, ( gcnew AssemblyName( args->Name ) )->Name + ".dll" ); 17 | if ( !File::Exists( assemblyPath ) ) 18 | { 19 | return nullptr; 20 | } 21 | 22 | return Assembly::Load( AssemblyName::GetAssemblyName( assemblyPath ) ); 23 | } 24 | 25 | } 26 | 27 | namespace Qwr::DotnetHost 28 | { 29 | 30 | void InitializeAssemblyLoader() 31 | { 32 | auto currentDomain = AppDomain::CurrentDomain; 33 | currentDomain->AssemblyResolve += gcnew ResolveEventHandler( ::LoadFromSameFolder ); 34 | } 35 | 36 | ComponentLoadContext::ComponentLoadContext( String ^ pluginPath ) 37 | : _resolver( pluginPath ) 38 | { 39 | } 40 | 41 | Assembly ^ ComponentLoadContext::Load( AssemblyName ^ assemblyName ) 42 | { 43 | auto assemblyPath = _resolver.ResolveAssemblyToPath( assemblyName ); 44 | if ( assemblyPath == nullptr ) 45 | { 46 | return nullptr; 47 | } 48 | 49 | return LoadFromAssemblyPath( assemblyPath ); 50 | } 51 | 52 | IntPtr ComponentLoadContext::LoadUnmanagedDll( String ^ unmanagedDllName ) 53 | { 54 | auto libraryPath = _resolver.ResolveUnmanagedDllToPath( unmanagedDllName ); 55 | if ( libraryPath == nullptr ) 56 | { 57 | return IntPtr::Zero; 58 | } 59 | 60 | return LoadUnmanagedDllFromPath( libraryPath ); 61 | } 62 | 63 | } // namespace Qwr::DotnetHost 64 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/loader/assembly_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::Reflection; 5 | using namespace System::Runtime::Loader; 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | void InitializeAssemblyLoader(); 11 | 12 | private 13 | ref class ComponentLoadContext : public AssemblyLoadContext 14 | { 15 | public: 16 | ComponentLoadContext( String ^ pluginPath ); 17 | 18 | protected: 19 | Assembly ^ Load( AssemblyName ^ assemblyName ) override; 20 | IntPtr LoadUnmanagedDll( String ^ unmanagedDllName ) override; 21 | 22 | private: 23 | AssemblyDependencyResolver _resolver; 24 | }; 25 | 26 | } // namespace Qwr::DotnetLoader 27 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/loader/component_loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "component_loader.h" 4 | 5 | #include 6 | 7 | using namespace System::Reflection; 8 | using namespace System; 9 | using namespace System::IO; 10 | 11 | namespace Qwr::DotnetHost 12 | { 13 | 14 | ComponentLoader::ComponentLoader() 15 | { 16 | InitializeAssemblyLoader(); 17 | } 18 | 19 | List ^ ComponentLoader::GetComponentsInDir( System::String ^ dirName ) 20 | { 21 | auto components = gcnew List(); 22 | 23 | if ( !Directory::Exists( dirName ) ) 24 | { 25 | return components; 26 | } 27 | 28 | for each ( auto d in Directory::EnumerateDirectories( dirName ) ) 29 | { 30 | auto componentDllPath = Path::Combine( d, Path::GetFileName( d ) + ".dll" ); 31 | if ( !File::Exists( componentDllPath ) ) 32 | { 33 | continue; 34 | } 35 | 36 | auto component = gcnew Component(); 37 | component->fullPath = componentDllPath; 38 | component->underscoredName = Path::GetFileNameWithoutExtension( componentDllPath ); 39 | components->Add( component ); 40 | } 41 | 42 | return components; 43 | } 44 | 45 | void ComponentLoader::LoadComponent( Component ^ component ) 46 | { 47 | auto fullPath = component->fullPath; 48 | assert( fullPath ); 49 | 50 | auto clientType = IComponent::typeid; 51 | auto loadContext = gcnew ComponentLoadContext( fullPath ); 52 | auto assembly = loadContext->LoadFromAssemblyName( AssemblyName::GetAssemblyName( fullPath ) ); 53 | 54 | for each ( Type ^ t in assembly->GetTypes() ) 55 | { 56 | if ( clientType->IsAssignableFrom( t ) ) 57 | { 58 | component->instance = CreateInstance( t ); 59 | return; 60 | } 61 | } 62 | 63 | throw gcnew Exception( "Failed to find component entry point: " + fullPath ); 64 | }; 65 | 66 | IComponent ^ ComponentLoader::CreateInstance( Type ^ type ) 67 | { 68 | auto ctorInfo = type->GetConstructor( Type::EmptyTypes ); 69 | if ( ctorInfo == nullptr ) 70 | { 71 | throw gcnew Exception( "Can't access component constructor" ); 72 | } 73 | 74 | auto ctorAttributes = ctorInfo->GetCustomAttributes( false ); 75 | auto interfaceAttributeType = ComponentInterfaceVersionAttribute::typeid; 76 | auto currentVersion = Assembly::GetAssembly( interfaceAttributeType )->GetName()->Version; 77 | 78 | ComponentInterfaceVersionAttribute ^ componentInterfaceAttribute = nullptr; 79 | for each ( auto attr in ctorAttributes ) 80 | { 81 | if ( attr->GetType() == interfaceAttributeType ) 82 | { 83 | componentInterfaceAttribute = dynamic_cast( attr ); 84 | break; 85 | } 86 | } 87 | if ( !componentInterfaceAttribute ) 88 | { 89 | throw gcnew Exception( "Component constructor is missing `ComponentInterfaceVersion` attribute" ); 90 | } 91 | 92 | auto supportedVersion = componentInterfaceAttribute->Version; 93 | if ( currentVersion->Major != supportedVersion->Major 94 | || currentVersion->Minor < supportedVersion->Minor ) 95 | { 96 | throw gcnew Exception( "Interface version mismatch:\n" 97 | + " Interface version requested by component: " + supportedVersion->ToString() + "\n" 98 | + " Host interface version: " + currentVersion->ToString() + "\n" 99 | + "Update " DNET_UNDERSCORE_NAME " to use this .NET component" ); 100 | } 101 | 102 | return dynamic_cast( ctorInfo->Invoke( nullptr ) ); 103 | } 104 | 105 | } // namespace Qwr::DotnetHost 106 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/loader/component_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using namespace System; 6 | using namespace System::Collections::Generic; 7 | using namespace Qwr::ComponentInterface; 8 | 9 | namespace Qwr::DotnetHost 10 | { 11 | 12 | private 13 | ref class ComponentLoader 14 | { 15 | public: 16 | ComponentLoader(); 17 | 18 | public: 19 | List ^ GetComponentsInDir( String ^ dirName ); 20 | void LoadComponent( Component ^ component ); 21 | 22 | private: 23 | IComponent ^ CreateInstance( Type ^ type ); 24 | }; 25 | 26 | } // namespace Qwr::DotnetHost 27 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_config_var.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb_config_var.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace 9 | { 10 | 11 | template 12 | Qwr::DotnetHost::NetFbConfigVar::ConfigT* InitializeCfg( const GUID& guid, const T2& defaultValue ) 13 | { 14 | return new Qwr::DotnetHost::NetFbConfigVar::ConfigT( std::in_place_type, guid, defaultValue ); 15 | } 16 | 17 | } // namespace 18 | 19 | namespace Qwr::DotnetHost 20 | { 21 | 22 | generic 23 | NetFbConfigVar::NetFbConfigVar( Guid ^ cfgGuid, T defaultValue ) 24 | : defaultValue_( defaultValue ) 25 | { 26 | if ( Object::ReferenceEquals( defaultValue, nullptr ) ) 27 | { 28 | throw gcnew ArgumentNullException( "defaultValue" ); 29 | } 30 | 31 | auto t = T::typeid; 32 | auto nativeGuid = Convert::ToNative::ToValue( cfgGuid ); 33 | if ( t == bool ::typeid ) 34 | { 35 | auto nativeDefaultValue = (bool)defaultValue; 36 | pConfig_ = InitializeCfg( nativeGuid, nativeDefaultValue ); 37 | } 38 | else if ( t == String::typeid ) 39 | { 40 | auto nativeDefaultValue = Convert::ToNative::ToValue( ( String ^ ) defaultValue ); 41 | pConfig_ = InitializeCfg( nativeGuid, nativeDefaultValue.c_str() ); 42 | } 43 | else if ( t == uint8_t::typeid ) 44 | { 45 | auto nativeDefaultValue = (uint8_t)defaultValue; 46 | pConfig_ = InitializeCfg>( nativeGuid, nativeDefaultValue ); 47 | } 48 | else if ( t == uint32_t::typeid ) 49 | { 50 | auto nativeDefaultValue = (uint32_t)defaultValue; 51 | pConfig_ = InitializeCfg>( nativeGuid, nativeDefaultValue ); 52 | } 53 | else if ( t == int8_t::typeid ) 54 | { 55 | auto nativeDefaultValue = (int8_t)defaultValue; 56 | pConfig_ = InitializeCfg>( nativeGuid, nativeDefaultValue ); 57 | } 58 | else if ( t == int32_t::typeid ) 59 | { 60 | auto nativeDefaultValue = (int32_t)defaultValue; 61 | pConfig_ = InitializeCfg>( nativeGuid, nativeDefaultValue ); 62 | } 63 | else 64 | { 65 | throw gcnew ArgumentException( "Supplied generic parameter type is not supported" ); 66 | } 67 | } 68 | 69 | generic 70 | NetFbConfigVar::~NetFbConfigVar() 71 | { 72 | this->!NetFbConfigVar(); 73 | } 74 | 75 | generic 76 | NetFbConfigVar::!NetFbConfigVar() 77 | { 78 | if ( pConfig_ ) 79 | { 80 | delete pConfig_; 81 | pConfig_ = nullptr; 82 | } 83 | } 84 | 85 | generic void NetFbConfigVar::Set( T value ) 86 | { 87 | assert( pConfig_ ); 88 | if ( auto pAlternativeConfig = std::get_if( pConfig_ ); 89 | pAlternativeConfig ) 90 | { 91 | *pAlternativeConfig = (bool)value; 92 | } 93 | else if ( auto pAlternativeConfig = std::get_if( pConfig_ ); 94 | pAlternativeConfig ) 95 | { 96 | *pAlternativeConfig = Convert::ToNative::ToValue( ( String ^ ) value ).c_str(); 97 | } 98 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 99 | pAlternativeConfig ) 100 | { 101 | *pAlternativeConfig = (uint8_t)value; 102 | } 103 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 104 | pAlternativeConfig ) 105 | { 106 | *pAlternativeConfig = (uint32_t)value; 107 | } 108 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 109 | pAlternativeConfig ) 110 | { 111 | *pAlternativeConfig = (int8_t)value; 112 | } 113 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 114 | pAlternativeConfig ) 115 | { 116 | *pAlternativeConfig = (int32_t)value; 117 | } 118 | else 119 | { 120 | assert( false ); 121 | throw gcnew Exception( "Internal error: not all variants were covered" ); 122 | } 123 | } 124 | 125 | generic 126 | T NetFbConfigVar::Get() 127 | { 128 | assert( pConfig_ ); 129 | if ( auto pAlternativeConfig = std::get_if( pConfig_ ); 130 | pAlternativeConfig ) 131 | { 132 | return (T)pAlternativeConfig->get_value(); 133 | } 134 | else if ( auto pAlternativeConfig = std::get_if( pConfig_ ); 135 | pAlternativeConfig ) 136 | { 137 | const std::string str = static_cast( *pAlternativeConfig ); 138 | return (T)Convert::ToNet::ToValue( str ); 139 | } 140 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 141 | pAlternativeConfig ) 142 | { 143 | return (T)pAlternativeConfig->get_value(); 144 | } 145 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 146 | pAlternativeConfig ) 147 | { 148 | return (T)pAlternativeConfig->get_value(); 149 | } 150 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 151 | pAlternativeConfig ) 152 | { 153 | return (T)pAlternativeConfig->get_value(); 154 | } 155 | else if ( auto pAlternativeConfig = std::get_if>( pConfig_ ); 156 | pAlternativeConfig ) 157 | { 158 | return (T)pAlternativeConfig->get_value(); 159 | } 160 | else 161 | { 162 | assert( false ); 163 | throw gcnew Exception( "Internal error: not all variants were covered" ); 164 | } 165 | } 166 | 167 | } // namespace Qwr::DotnetHost 168 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_config_var.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using namespace System; 6 | using namespace Qwr::ComponentInterface; 7 | 8 | namespace Qwr::DotnetHost 9 | { 10 | 11 | generic private ref class NetFbConfigVar sealed : public IConfigVar 12 | { 13 | using ConfigT = std::variant, cfg_int_t, cfg_int_t, cfg_int_t>; 14 | 15 | public: 16 | NetFbConfigVar( Guid ^ cfgGuid, T defaultValue ); 17 | ~NetFbConfigVar(); 18 | !NetFbConfigVar(); 19 | 20 | virtual void Set( T value ); 21 | virtual T Get(); 22 | 23 | private: 24 | T defaultValue_; 25 | ConfigT* pConfig_ = nullptr; 26 | }; 27 | 28 | } // namespace Qwr::DotnetHost 29 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_console.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb_console.h" 4 | 5 | #include 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | void NetFbConsole::Log( String ^ text ) 11 | { 12 | FB2K_console_formatter() << Convert::ToNative::ToValue( text ).c_str(); 13 | } 14 | 15 | } // namespace Qwr::DotnetHost -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | private 10 | ref class NetFbConsole sealed : public IConsole 11 | { 12 | public: 13 | virtual void Log( String ^ text ); 14 | }; 15 | 16 | } // namespace Qwr::DotnetHost 17 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_file_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::Collections::Generic; 5 | using namespace Qwr::ComponentInterface; 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | private 11 | ref class NetFbFileInfo sealed : IFileInfo 12 | { 13 | public: 14 | NetFbFileInfo( metadb_info_container::ptr containerInfo ); 15 | ~NetFbFileInfo(); 16 | !NetFbFileInfo(); 17 | 18 | public: 19 | virtual IEnumerable ^ InfoEnum(); 20 | virtual IEnumerable ^ MetaEnum(); 21 | 22 | const file_info& FileInfo(); 23 | 24 | private: 25 | metadb_info_container::ptr* pContainerInfo_ = nullptr; 26 | const file_info& fileInfo_; 27 | }; 28 | 29 | } // namespace Qwr::DotnetHost 30 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_main_menu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb_main_menu.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Qwr::DotnetHost 10 | { 11 | 12 | NetFbMainMenuCommand::NetFbMainMenuCommand( Guid guid, String ^ name, String ^ description, Action ^ action ) 13 | : guid_( guid ) 14 | , name_( name ) 15 | , description_( description ) 16 | , action_( action ) 17 | { 18 | } 19 | 20 | bool NetFbMainMenuCommand::IsAlwaysHidden::get() 21 | { 22 | return isAlwaysHidden_; 23 | } 24 | 25 | void NetFbMainMenuCommand::IsAlwaysHidden::set( bool value ) 26 | { 27 | isAlwaysHidden_ = value; 28 | } 29 | 30 | bool NetFbMainMenuCommand::IsChecked::get() 31 | { 32 | return isChecked_; 33 | } 34 | 35 | void NetFbMainMenuCommand::IsChecked::set( bool value ) 36 | { 37 | isChecked_ = value; 38 | } 39 | 40 | bool NetFbMainMenuCommand::IsDefaultHidden::get() 41 | { 42 | return isDefaultHidden_; 43 | } 44 | 45 | void NetFbMainMenuCommand::IsDefaultHidden::set( bool value ) 46 | { 47 | isDefaultHidden_ = value; 48 | } 49 | 50 | bool NetFbMainMenuCommand::IsEnabled::get() 51 | { 52 | return isEnabled_; 53 | } 54 | 55 | void NetFbMainMenuCommand::IsEnabled::set( bool value ) 56 | { 57 | isEnabled_ = value; 58 | } 59 | 60 | bool NetFbMainMenuCommand::IsRadioChecked::get() 61 | { 62 | return isRadioChecked_; 63 | } 64 | 65 | void NetFbMainMenuCommand::IsRadioChecked::set( bool value ) 66 | { 67 | isRadioChecked_ = value; 68 | } 69 | 70 | Guid ^ NetFbMainMenuCommand::GetGuid() 71 | { 72 | return guid_; 73 | } 74 | 75 | String ^ NetFbMainMenuCommand::GetName() 76 | { 77 | return name_; 78 | } 79 | 80 | String ^ NetFbMainMenuCommand::GetDescription() 81 | { 82 | return description_; 83 | } 84 | 85 | Action ^ NetFbMainMenuCommand::GetAction() 86 | { 87 | return action_; 88 | } 89 | 90 | NetFbMainMenuCommandSection::NetFbMainMenuCommandSection( Guid parentGuid, Nullable sortPriority ) 91 | : childCommands_( gcnew List() ) 92 | { 93 | RegisterMainMenuCommandSection( Convert::ToNative::ToValue( parentGuid ), sortPriority.HasValue ? sortPriority.Value << 16 : mainmenu_commands::sort_priority_dontcare, childCommands_ ); 94 | } 95 | 96 | IMainMenuCommand ^ NetFbMainMenuCommandSection::AddCommand( Guid guid, String ^ name, String ^ description, Action ^ action ) 97 | { 98 | if ( Host::GetInstance()->IsInitialized() ) 99 | { 100 | throw gcnew Exception( "Static services can't be initialized after the component start" ); 101 | } 102 | 103 | auto command = gcnew NetFbMainMenuCommand( guid, name, description, action ); 104 | childCommands_->Add( command ); 105 | return command; 106 | } 107 | 108 | NetFbMainMenuGroup::NetFbMainMenuGroup( Guid guid ) 109 | : guid_( guid ) 110 | , childGroups_( gcnew List() ) 111 | , childCommands_( gcnew List() ) 112 | { 113 | } 114 | 115 | NetFbMainMenuGroup::NetFbMainMenuGroup( Guid guid, Guid parentGuid, String ^ name, Nullable sortPriority, bool isPopup ) 116 | : guid_( guid ) 117 | , childGroups_( gcnew List() ) 118 | , childCommands_( gcnew List() ) 119 | { 120 | RegisterMainMenuGroup( Convert::ToNative::ToValue( guid ), 121 | Convert::ToNative::ToValue( parentGuid ), 122 | Convert::ToNative::ToValue( name ), 123 | sortPriority.HasValue ? sortPriority.Value << 16 : mainmenu_commands::sort_priority_dontcare, 124 | isPopup ); 125 | } 126 | 127 | IMainMenuGroup ^ NetFbMainMenuGroup::AddGroup( Guid guid, String ^ name, Nullable sortPriority, bool isPopup ) 128 | { 129 | if ( Host::GetInstance()->IsInitialized() ) 130 | { 131 | throw gcnew Exception( "Static services can't be initialized after the component start" ); 132 | } 133 | 134 | auto command = gcnew NetFbMainMenuGroup( guid, guid_, name, sortPriority, isPopup ); 135 | childGroups_->Add( command ); 136 | return command; 137 | } 138 | 139 | IMainMenuCommandSection ^ NetFbMainMenuGroup::AddCommandSection( Nullable sortPriority ) 140 | { 141 | if ( Host::GetInstance()->IsInitialized() ) 142 | { 143 | throw gcnew Exception( "Static services can't be initialized after the component start" ); 144 | } 145 | 146 | auto command = gcnew NetFbMainMenuCommandSection( guid_, sortPriority ); 147 | childCommands_->Add( command ); 148 | return command; 149 | } 150 | 151 | } // namespace Qwr::DotnetHost 152 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_main_menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::Collections::Generic; 5 | using namespace Qwr::ComponentInterface; 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | private 11 | ref class NetFbMainMenuCommand sealed : public IMainMenuCommand 12 | { 13 | public: 14 | NetFbMainMenuCommand( Guid guid, String ^ name, String ^ description, Action ^ action ); 15 | 16 | virtual property bool IsAlwaysHidden 17 | { 18 | bool get(); 19 | void set( bool value ); 20 | } 21 | virtual property bool IsChecked 22 | { 23 | bool get(); 24 | void set( bool value ); 25 | } 26 | virtual property bool IsDefaultHidden 27 | { 28 | bool get(); 29 | void set( bool value ); 30 | } 31 | virtual property bool IsEnabled 32 | { 33 | bool get(); 34 | void set( bool value ); 35 | } 36 | virtual property bool IsRadioChecked 37 | { 38 | bool get(); 39 | void set( bool value ); 40 | } 41 | 42 | public: 43 | Guid ^ GetGuid(); 44 | String ^ GetName(); 45 | String ^ GetDescription(); 46 | Action ^ GetAction(); 47 | 48 | private: 49 | Guid ^ guid_; 50 | String ^ name_; 51 | String ^ description_; 52 | Action ^ action_; 53 | 54 | bool isEnabled_ = true; 55 | bool isDefaultHidden_ = false; 56 | bool isAlwaysHidden_ = false; 57 | bool isChecked_ = false; 58 | bool isRadioChecked_ = false; 59 | }; 60 | 61 | private 62 | ref class NetFbMainMenuCommandSection sealed : public IMainMenuCommandSection 63 | { 64 | public: 65 | NetFbMainMenuCommandSection( Guid parentGuid, Nullable sortPriority ); 66 | 67 | virtual IMainMenuCommand ^ AddCommand( Guid guid, String ^ name, String ^ description, Action ^ action ); 68 | 69 | private: 70 | List ^ childCommands_; 71 | }; 72 | 73 | private 74 | ref class NetFbMainMenuGroup sealed : public IMainMenuGroup 75 | { 76 | public: 77 | NetFbMainMenuGroup( Guid guid ); 78 | NetFbMainMenuGroup( Guid guid, Guid parentGuid, String ^ name, Nullable sortPriority, bool isPopup ); 79 | 80 | virtual IMainMenuGroup ^ AddGroup( Guid guid, String ^ name, Nullable sortPriority, bool isPopup ); 81 | virtual IMainMenuCommandSection ^ AddCommandSection( Nullable sortPriority ); 82 | 83 | private: 84 | Guid guid_; 85 | 86 | List ^ childGroups_; 87 | List ^ childCommands_; 88 | }; 89 | 90 | } // namespace Qwr::DotnetHost 91 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_metadb_handle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fb_metadb_handle.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace 10 | { 11 | 12 | const std::array gKnownArtGuids = { 13 | &album_art_ids::cover_front, 14 | &album_art_ids::cover_back, 15 | &album_art_ids::disc, 16 | &album_art_ids::icon, 17 | &album_art_ids::artist 18 | }; 19 | } 20 | 21 | namespace 22 | { 23 | 24 | Bitmap ^ ExtractBitmap( album_art_extractor_instance_v2::ptr pExtractor, const GUID& artTypeGuid, abort_callback& abort ) { 25 | auto pArtData = pExtractor->query( artTypeGuid, abort ); 26 | if ( !pArtData.is_valid() ) 27 | { 28 | return nullptr; 29 | } 30 | 31 | auto stream = gcnew System::IO::UnmanagedMemoryStream( (unsigned char*)pArtData->get_ptr(), pArtData->get_size() ); 32 | Bitmap ^ netBitmap = gcnew Bitmap( stream ); 33 | delete stream; 34 | return netBitmap; 35 | }; 36 | 37 | } 38 | 39 | namespace Qwr::DotnetHost 40 | { 41 | 42 | NetFbMetadbHandle::NetFbMetadbHandle( const metadb_handle_ptr& metadbHandle ) 43 | : pMetadbHandle_( new metadb_handle_ptr(metadbHandle) ) 44 | { 45 | } 46 | 47 | NetFbMetadbHandle::~NetFbMetadbHandle() 48 | { 49 | this->!NetFbMetadbHandle(); 50 | } 51 | 52 | NetFbMetadbHandle::!NetFbMetadbHandle() 53 | { 54 | if ( !core_api::are_services_available() ) 55 | { 56 | return; 57 | } 58 | 59 | if ( pMetadbHandle_ ) 60 | { 61 | delete pMetadbHandle_; 62 | pMetadbHandle_ = nullptr; 63 | } 64 | } 65 | 66 | Bitmap ^ NetFbMetadbHandle::Artwork( ArtId artId ) 67 | { 68 | assert( pMetadbHandle_ ); 69 | 70 | if ( static_cast(artId) >= gKnownArtGuids.size() ) 71 | { 72 | throw gcnew ArgumentException( "Unknown art id: " + artId.ToString(), "artId" ); 73 | } 74 | const auto& artTypeGuid = *gKnownArtGuids[static_cast( artId )]; 75 | 76 | abort_callback_dummy abort; 77 | auto aamv2 = album_art_manager_v2::get(); 78 | 79 | try 80 | { 81 | auto aaeiv2 = aamv2->open( pfc::list_single_ref_t( *pMetadbHandle_ ), pfc::list_single_ref_t( artTypeGuid ), abort ); 82 | return ExtractBitmap( aaeiv2, artTypeGuid, abort ); 83 | } 84 | catch ( const pfc::exception& ) 85 | { // not found or aborted 86 | } 87 | 88 | return nullptr; 89 | } 90 | 91 | Bitmap ^ NetFbMetadbHandle::ArtworkStub( ArtId artId ) 92 | { 93 | assert( pMetadbHandle_ ); 94 | 95 | if ( static_cast( artId ) >= gKnownArtGuids.size() ) 96 | { 97 | throw gcnew ArgumentException( "Unknown art id: " + artId.ToString(), "artId" ); 98 | } 99 | const auto& artTypeGuid = *gKnownArtGuids[static_cast( artId )]; 100 | 101 | abort_callback_dummy abort; 102 | auto aamv2 = album_art_manager_v2::get(); 103 | 104 | try 105 | { 106 | auto aaeiv2 = aamv2->open_stub( abort ); 107 | return ExtractBitmap( aaeiv2, artTypeGuid, abort ); 108 | } 109 | catch ( const pfc::exception& ) 110 | { // not found or aborted 111 | } 112 | 113 | return nullptr; 114 | } 115 | 116 | IFileInfo ^ NetFbMetadbHandle::FileInfo() 117 | { 118 | return gcnew NetFbFileInfo( (*pMetadbHandle_)->get_info_ref() ); 119 | } 120 | 121 | String ^NetFbMetadbHandle::Path() 122 | { 123 | assert( pMetadbHandle_ ); 124 | 125 | const auto path = ( *pMetadbHandle_ )->get_path(); 126 | return Convert::ToNet::ToValue( std::string_view{ path, strlen( path ) } ); 127 | } 128 | 129 | double NetFbMetadbHandle::Length() 130 | { 131 | assert( pMetadbHandle_ ); 132 | return ( *pMetadbHandle_ )->get_length(); 133 | } 134 | 135 | metadb_handle_ptr NetFbMetadbHandle::Handle() 136 | { 137 | assert( pMetadbHandle_ ); 138 | return *pMetadbHandle_; 139 | } 140 | 141 | } // namespace fooManagedWrapper 142 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_metadb_handle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::Drawing; 5 | using namespace Qwr::ComponentInterface; 6 | 7 | namespace Qwr::DotnetHost 8 | { 9 | 10 | private 11 | ref class NetFbMetadbHandle sealed : IMetadbHandle 12 | { 13 | public: 14 | NetFbMetadbHandle( const metadb_handle_ptr& metadbHandle ); 15 | ~NetFbMetadbHandle(); 16 | !NetFbMetadbHandle(); 17 | 18 | public: 19 | virtual Bitmap ^ Artwork( ArtId artIdb ); 20 | virtual Bitmap ^ ArtworkStub( ArtId artId ); 21 | virtual IFileInfo ^ FileInfo(); 22 | virtual double Length(); 23 | virtual String ^ Path(); 24 | 25 | internal : metadb_handle_ptr Handle(); 26 | 27 | private: 28 | metadb_handle_ptr* pMetadbHandle_ = nullptr; 29 | }; 30 | 31 | } // namespace Qwr::DotnetHost 32 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_play_callback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fb_play_callback.h" 3 | 4 | #include 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | NetFbPlayCallback::NetFbPlayCallback() 10 | : pImpl_( new PlayCallbackImpl(this) ) 11 | { 12 | } 13 | 14 | NetFbPlayCallback::~NetFbPlayCallback() 15 | { 16 | this->!NetFbPlayCallback(); 17 | } 18 | 19 | NetFbPlayCallback::!NetFbPlayCallback() 20 | { 21 | if ( pImpl_ ) 22 | { 23 | delete pImpl_; 24 | pImpl_ = nullptr; 25 | } 26 | } 27 | 28 | } // namespace Qwr::DotnetHost 29 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_play_callback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | #define QWR_CALLBACK_VOID( name ) \ 7 | virtual event EventHandler ^ name; \ 8 | void On##name() \ 9 | { \ 10 | ##name( this, EventArgs::Empty ); \ 11 | } 12 | 13 | #define QWR_CALLBACK( name, type ) \ 14 | virtual event EventHandler ^ name; \ 15 | void On##name( type value ) \ 16 | { \ 17 | ##name( this, value ); \ 18 | } 19 | 20 | #define QWR_GENERIC_CALLBACK( name, type ) \ 21 | virtual event EventHandler ^> ^ name; \ 22 | void On##name( type value ) \ 23 | { \ 24 | ##name( this, gcnew GenericEventArgs( value ) ); \ 25 | } 26 | 27 | namespace Qwr::DotnetHost 28 | { 29 | 30 | class PlayCallbackImpl; 31 | 32 | private 33 | ref class NetFbPlayCallback sealed : public IPlaybackCallbacks 34 | { 35 | public: 36 | NetFbPlayCallback(); 37 | ~NetFbPlayCallback(); 38 | !NetFbPlayCallback(); 39 | 40 | public: 41 | QWR_GENERIC_CALLBACK( PlaybackAdvancedToNewTrack, IMetadbHandle ^ ); 42 | QWR_CALLBACK( PlaybackStarting, PlaybackStartingEventArgs ^ ); 43 | QWR_GENERIC_CALLBACK( PlaybackStopped, PlaybackStopReason ); 44 | QWR_GENERIC_CALLBACK( PlaybackPausedStateChanged, bool ); 45 | QWR_CALLBACK_VOID( DynamicPlaybackTrackInfoChanged ); 46 | QWR_CALLBACK_VOID( DynamicTrackInfoChanged ); 47 | QWR_GENERIC_CALLBACK( CurrentTrackInfoChanged, IMetadbHandle ^ ); 48 | QWR_GENERIC_CALLBACK( TrackPlaybackPositionChanged, double ); 49 | QWR_GENERIC_CALLBACK( TrackSeekPerformed, double ); 50 | QWR_GENERIC_CALLBACK( VolumeChanged, float ); 51 | 52 | private: 53 | PlayCallbackImpl* pImpl_ = nullptr; 54 | }; 55 | 56 | } // namespace Qwr::DotnetHost 57 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_play_control.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fb_play_control.h" 3 | 4 | #include 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | 10 | IMetadbHandle ^ NetFbPlayControl::NowPlaying() 11 | { 12 | metadb_handle_ptr metadb; 13 | if ( !playback_control::get()->get_now_playing( metadb ) ) 14 | { 15 | return nullptr; 16 | } 17 | 18 | return gcnew NetFbMetadbHandle( metadb ); 19 | } 20 | 21 | double NetFbPlayControl::TrackPlaybackPosition() 22 | { 23 | return playback_control::get()->playback_get_position(); 24 | } 25 | 26 | bool NetFbPlayControl::IsPlaying() 27 | { 28 | return playback_control::get()->is_playing(); 29 | } 30 | 31 | bool NetFbPlayControl::IsPaused() 32 | { 33 | return playback_control::get()->is_paused(); 34 | } 35 | 36 | void NetFbPlayControl::Play() 37 | { 38 | standard_commands::main_play(); 39 | } 40 | 41 | void NetFbPlayControl::Pause() 42 | { 43 | standard_commands::main_pause(); 44 | } 45 | 46 | void NetFbPlayControl::Next() 47 | { 48 | standard_commands::main_next(); 49 | } 50 | 51 | void NetFbPlayControl::Prev() 52 | { 53 | standard_commands::main_previous(); 54 | } 55 | 56 | } // namespace fooManagedWrapper -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_play_control.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | private 10 | ref class NetFbPlayControl sealed : public IPlaybackControls 11 | { 12 | public: 13 | virtual IMetadbHandle ^ NowPlaying(); 14 | virtual double TrackPlaybackPosition(); 15 | virtual bool IsPlaying(); 16 | virtual bool IsPaused(); 17 | virtual void Play(); 18 | virtual void Pause(); 19 | virtual void Next(); 20 | virtual void Prev(); 21 | }; 22 | 23 | } // namespace Qwr::DotnetHost 24 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_preferences_page_callback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb_preferences_page_callback.h" 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | NetFbPreferencesPageCallback::NetFbPreferencesPageCallback( preferences_page_callback::ptr& callback ) 9 | : pCallback_( new preferences_page_callback::ptr( callback ) ) 10 | { 11 | } 12 | 13 | NetFbPreferencesPageCallback::~NetFbPreferencesPageCallback() 14 | { 15 | this->!NetFbPreferencesPageCallback(); 16 | } 17 | 18 | NetFbPreferencesPageCallback::!NetFbPreferencesPageCallback() 19 | { 20 | if ( pCallback_ ) 21 | { 22 | delete pCallback_; 23 | pCallback_ = nullptr; 24 | } 25 | } 26 | 27 | void NetFbPreferencesPageCallback::OnStateChanged() 28 | { 29 | if ( pCallback_ ) 30 | { 31 | ( *pCallback_ )->on_state_changed(); 32 | } 33 | } 34 | 35 | } // namespace Qwr::DotnetHost 36 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_preferences_page_callback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | private 10 | ref class NetFbPreferencesPageCallback sealed : public IPreferencesPageCallback 11 | { 12 | public: 13 | NetFbPreferencesPageCallback( preferences_page_callback::ptr& callback ); 14 | ~NetFbPreferencesPageCallback(); 15 | !NetFbPreferencesPageCallback(); 16 | 17 | virtual void OnStateChanged(); 18 | 19 | private: 20 | preferences_page_callback::ptr* pCallback_; 21 | }; 22 | 23 | } // namespace Qwr::DotnetHost 24 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_title_format.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "fb_title_format.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace Qwr::DotnetHost 11 | { 12 | 13 | NetFbTitleFormat::NetFbTitleFormat( String ^ expression ) 14 | : pTitleFormatObject_( new titleformat_object::ptr() ) 15 | { 16 | titleformat_compiler::get()->compile_safe( *pTitleFormatObject_, Convert::ToNative::ToValue( expression ).c_str() ); 17 | } 18 | 19 | NetFbTitleFormat::~NetFbTitleFormat() 20 | { 21 | this->!NetFbTitleFormat(); 22 | } 23 | 24 | NetFbTitleFormat::!NetFbTitleFormat() 25 | { 26 | if ( pTitleFormatObject_ ) 27 | { 28 | delete pTitleFormatObject_; 29 | pTitleFormatObject_ = nullptr; 30 | } 31 | } 32 | 33 | String ^ NetFbTitleFormat::Eval( bool force ) 34 | { 35 | assert( pTitleFormatObject_ ); 36 | 37 | auto pc = playback_control::get(); 38 | metadb_handle_ptr handle; 39 | 40 | if ( !pc->is_playing() && force ) 41 | { // Trying to get handle to any known playable location 42 | if ( !metadb::g_get_random_handle( handle ) ) 43 | { // Fake handle, workaround recommended by foobar2000 devs 44 | metadb::get()->handle_create( handle, playable_location_impl{} ); 45 | } 46 | } 47 | 48 | pfc::string8_fast text; 49 | pc->playback_format_title_ex( handle, nullptr, text, *pTitleFormatObject_, nullptr, playback_control::display_level_all ); 50 | return Convert::ToNet::ToValue(text); 51 | } 52 | 53 | String ^ NetFbTitleFormat::EvalWithMetadb( IMetadbHandle ^ metadbHandle ) 54 | { 55 | assert( pTitleFormatObject_ ); 56 | 57 | auto concreteMetadb = Convert::ToNative::ToValue( metadbHandle ); 58 | 59 | pfc::string8_fast text; 60 | concreteMetadb->format_title( nullptr, text, *pTitleFormatObject_, nullptr ); 61 | return Convert::ToNet::ToValue( text ); 62 | } 63 | 64 | } // namespace Qwr::DotnetHost 65 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/fb_title_format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | private 10 | ref class NetFbTitleFormat sealed : public ITitleFormat 11 | { 12 | public: 13 | NetFbTitleFormat( String ^ expression ); 14 | ~NetFbTitleFormat(); 15 | !NetFbTitleFormat(); 16 | 17 | public: 18 | virtual String ^ Eval( bool force ); 19 | virtual String ^ EvalWithMetadb( IMetadbHandle ^ metadbHandle ); 20 | 21 | private: 22 | titleformat_object::ptr* pTitleFormatObject_ = nullptr; 23 | }; 24 | 25 | } // namespace Qwr::DotnetHost 26 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_objects/impl/enumerable_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Qwr::DotnetHost 4 | { 5 | 6 | template 7 | private interface class IEnumeratorFactory 8 | { 9 | virtual IEnumerator ^ Generate(); 10 | }; 11 | 12 | template 13 | private ref class EnumerableImpl : IEnumerable 14 | { 15 | public: 16 | EnumerableImpl( IEnumeratorFactory ^ enumFactory ) 17 | : enumFactory_( enumFactory ) 18 | { 19 | } 20 | 21 | virtual IEnumerator ^ GetEnumerator() { 22 | // FIX: replace with factory 23 | return enumFactory_->Generate(); 24 | } 25 | 26 | // clang-format off 27 | internal: 28 | virtual Collections::IEnumerator^ GetEnumerator1() = Collections::IEnumerable::GetEnumerator { 29 | return GetEnumerator(); 30 | }; 31 | 32 | // clang-format on 33 | 34 | private: 35 | IEnumeratorFactory ^ enumFactory_; 36 | }; 37 | 38 | template 39 | EnumerableImpl ^ MakeEnumerable( IEnumeratorFactory ^ enumFactory ) { 40 | return gcnew EnumerableImpl( enumFactory ); 41 | } 42 | 43 | } // namespace Qwr::DotnetHost 44 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/net_utils/exception_generator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | template 9 | T^ GenerateException( std::string_view message, Args ... args ) 10 | { 11 | return gcnew T( Convert::ToNet::ToValue( message ), args... ); 12 | } 13 | 14 | template 15 | T ^ GenerateException( pfc::exception e, Args... args ) { 16 | return gcnew T( Convert::ToNet::ToValue( e ), args... ); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /foo_dotnet_component_host/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/resources/foo_dotnet_component_host.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1251) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\r\n" 37 | "#include \r\n" 38 | "\0" 39 | END 40 | 41 | 3 TEXTINCLUDE 42 | BEGIN 43 | "\r\n" 44 | "\0" 45 | END 46 | 47 | #endif // APSTUDIO_INVOKED 48 | 49 | #endif // English (United States) resources 50 | ///////////////////////////////////////////////////////////////////////////// 51 | 52 | 53 | 54 | #ifndef APSTUDIO_INVOKED 55 | ///////////////////////////////////////////////////////////////////////////// 56 | // 57 | // Generated from the TEXTINCLUDE 3 resource. 58 | // 59 | 60 | 61 | ///////////////////////////////////////////////////////////////////////////// 62 | #endif // not APSTUDIO_INVOKED 63 | 64 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/resources/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by foo_dotnet_component_host.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | # ifndef APSTUDIO_READONLY_SYMBOLS 9 | # define _APS_NEXT_RESOURCE_VALUE 101 10 | # define _APS_NEXT_COMMAND_VALUE 40001 11 | # define _APS_NEXT_CONTROL_VALUE 1001 12 | # define _APS_NEXT_SYMED_VALUE 101 13 | # endif 14 | #endif 15 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/resources/version.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | 7 | #define APSTUDIO_HIDDEN_SYMBOLS 8 | #include "windows.h" 9 | #undef APSTUDIO_HIDDEN_SYMBOLS 10 | 11 | #include "../component_defines.h" 12 | 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // Version 26 | // 27 | 28 | VS_VERSION_INFO VERSIONINFO 29 | FILEVERSION DNET_VERSION_MAJOR,DNET_VERSION_MINOR,DNET_VERSION_PATCH,0 30 | PRODUCTVERSION DNET_VERSION_MAJOR,DNET_VERSION_MINOR,DNET_VERSION_PATCH,0 31 | FILEFLAGSMASK 0x3fL 32 | #ifdef _DEBUG 33 | #ifdef DNET_VERSION_PRERELEASE_TEXT 34 | FILEFLAGS 0x3L 35 | #else 36 | FILEFLAGS 0x1L 37 | #endif 38 | #else 39 | #ifdef DNET_VERSION_PRERELEASE_TEXT 40 | FILEFLAGS 0x2L 41 | #else 42 | FILEFLAGS 0x0L 43 | #endif 44 | #endif 45 | FILEOS 0x40004L 46 | FILETYPE 0x2L 47 | FILESUBTYPE 0x0L 48 | BEGIN 49 | BLOCK "StringFileInfo" 50 | BEGIN 51 | BLOCK "040904b0" 52 | BEGIN 53 | VALUE "FileDescription", DNET_NAME " component for foobar2000" 54 | VALUE "FileVersion", DNET_VERSION 55 | VALUE "InternalName", DNET_DLL_NAME 56 | VALUE "LegalCopyright", "Copyright (C) 2021 Yuri Shutenko" 57 | VALUE "OriginalFilename", DNET_DLL_NAME 58 | VALUE "ProductName", DNET_NAME 59 | VALUE "ProductVersion", DNET_VERSION 60 | END 61 | END 62 | BLOCK "VarFileInfo" 63 | BEGIN 64 | VALUE "Translation", 0x409, 1200 65 | END 66 | END 67 | 68 | #endif // English (United States) resources 69 | ///////////////////////////////////////////////////////////////////////////// 70 | 71 | 72 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // clang-format off 4 | 5 | #define NOMINMAX 6 | 7 | #ifdef _DEBUG 8 | # define QWR_TRACK_LEAKS 9 | # define CRTDBG_MAP_ALLOC 10 | # include 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #pragma unmanaged 17 | 18 | // foobar2000 SDK 19 | #pragma warning( push, 0 ) 20 | # include 21 | # include 22 | #pragma warning( pop ) 23 | 24 | // fmt 25 | #define FMT_HEADER_ONLY 26 | #include 27 | /// wchar_t support 28 | #include 29 | 30 | // json 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #pragma managed 39 | 40 | #include 41 | 42 | #include 43 | #include 44 | 45 | // clang-format on 46 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/ui/ui_preferences.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ui_preferences.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace Qwr::DotnetHost 9 | { 10 | 11 | ComponentInterface::PreferencesPageInfo Preferences::GetInfo() 12 | { 13 | PreferencesPageInfo info; 14 | info.Name = DNET_NAME; 15 | info.Guid = Convert::ToNet::ToValue( Guids::ui_preferences ); 16 | info.ParentGuid = Convert::ToNet::ToValue( preferences_page::guid_components ); 17 | info.HelpUrl = "https://theqwertiest.github.io/foo_dotnet_component_host/"; 18 | 19 | return info; 20 | } 21 | 22 | void Preferences::Initialize( IntPtr parentHandle, IPreferencesPageCallback ^ callback ) 23 | { 24 | preferencesCallback_ = callback; 25 | 26 | _impl = gcnew PreferencesForm( this ); 27 | SetParent( (HWND)_impl->Handle.ToPointer(), (HWND)parentHandle.ToPointer() ); 28 | _impl->Anchor = ( AnchorStyles::Left | AnchorStyles::Top | AnchorStyles::Right | AnchorStyles::Bottom ); 29 | _impl->Show(); 30 | } 31 | 32 | void Preferences::Reset() 33 | { 34 | } 35 | 36 | void Preferences::Apply() 37 | { 38 | assert( _impl ); 39 | 40 | _impl->Apply(); 41 | } 42 | 43 | PreferencesPageState Preferences::State() 44 | { 45 | assert( _impl ); 46 | 47 | PreferencesPageState state = PreferencesPageState::HasNoChanges; 48 | if ( _impl->HasComponentChanges() ) 49 | { 50 | state = PreferencesPageState::HasChanged | PreferencesPageState::NeedsFb2kRestart; 51 | } 52 | 53 | return state; 54 | } 55 | 56 | IntPtr Preferences::Handle() 57 | { 58 | assert( _impl ); 59 | return _impl->Handle; 60 | } 61 | 62 | IPreferencesPageCallback ^ Preferences::Callback() 63 | { 64 | return preferencesCallback_; 65 | } 66 | 67 | } // namespace Qwr::DotnetHost 68 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/ui/ui_preferences.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace Qwr::ComponentInterface; 5 | 6 | namespace Qwr::DotnetHost 7 | { 8 | 9 | ref class PreferencesForm; 10 | 11 | public 12 | ref class Preferences : IPreferencesPage 13 | { 14 | public: 15 | static PreferencesPageInfo GetInfo(); 16 | 17 | virtual void Initialize( IntPtr parentHandle, IPreferencesPageCallback ^ callback ); 18 | virtual void Reset(); 19 | virtual void Apply(); 20 | virtual PreferencesPageState State(); 21 | virtual IntPtr Handle(); 22 | 23 | IPreferencesPageCallback ^ Callback(); 24 | 25 | private: 26 | PreferencesForm ^ _impl; 27 | IPreferencesPageCallback ^ preferencesCallback_; 28 | }; 29 | 30 | } // namespace Qwr::DotnetHost 31 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/ui/ui_preferences_form.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | using namespace System::ComponentModel; 5 | using namespace System::Collections; 6 | using namespace System::Windows::Forms; 7 | using namespace System::Data; 8 | using namespace System::Drawing; 9 | 10 | namespace Qwr::DotnetHost 11 | { 12 | 13 | ref class Preferences; 14 | ref class ColumnSorter; 15 | ref class ComponentListEntry; 16 | 17 | private 18 | ref class PreferencesForm : public System::Windows::Forms::UserControl 19 | { 20 | public: 21 | PreferencesForm( Preferences ^ parent ); 22 | 23 | bool HasComponentChanges(); 24 | void Apply(); 25 | 26 | protected: 27 | ~PreferencesForm(); 28 | 29 | private: 30 | void Form_HandleCreated( Object ^ sender, EventArgs ^ e ); 31 | void Form_HandleDestroyed( Object ^ sender, EventArgs ^ e ); 32 | void ComponentList_MouseClick_EventHandler( Object ^ o, MouseEventArgs ^ e ); 33 | void ComponentList_MouseDoubleClick_EventHandler( Object ^ o, MouseEventArgs ^ e ); 34 | void ComponentList_ColumnClick_EventHandler( Object ^ o, ColumnClickEventArgs ^ e ); 35 | void ComponentList_DragEnter_EventHandler( Object ^ sender, DragEventArgs ^ e ); 36 | void ComponentList_DragDrop_EventHandler( Object ^ sender, DragEventArgs ^ e ); 37 | void ComponentListMenu_About_EventHandler( Object ^ sender, EventArgs ^ e ); 38 | void ComponentListMenu_Remove_EventHandler( Object ^ sender, EventArgs ^ e ); 39 | void InstallButton_Click_EventHandler( Object ^ sender, EventArgs ^ e ); 40 | void AdjustSizeComponentList( ListView ^ lv ); 41 | void InstallComponents( array ^ files ); 42 | 43 | private: 44 | /// 45 | /// Required method for Designer support - do not modify 46 | /// the contents of this method with the code editor-> 47 | /// 48 | void InitializeComponent( void ); 49 | 50 | private: 51 | System::ComponentModel::Container ^ components; 52 | System::Windows::Forms::TableLayoutPanel ^ topTableLayout; 53 | System::Windows::Forms::TableLayoutPanel ^ bottomTableLayout; 54 | System::Windows::Forms::ListView ^ componentList; 55 | System::Windows::Forms::ColumnHeader ^ nameColumn; 56 | System::Windows::Forms::ColumnHeader ^ versionColumn; 57 | System::Windows::Forms::ColumnHeader ^ moduleColumn; 58 | System::Windows::Forms::Label ^ leftLabel; 59 | System::Windows::Forms::Label ^ rightLabel; 60 | System::Windows::Forms::Button ^ installButton; 61 | 62 | private: 63 | Preferences ^ parent_ = nullptr; 64 | ColumnSorter ^ columnSorter_ = nullptr; 65 | ComponentListEntry ^ focusedItem_ = nullptr; 66 | 67 | bool hasComponentChanges_ = false; 68 | bool changesApplied_ = false; 69 | }; 70 | } // namespace Qwr::DotnetHost 71 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/ui/ui_preferences_form.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/utils/delayed_log.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "delayed_log.h" 4 | 5 | #include 6 | 7 | #pragma unmanaged 8 | 9 | namespace 10 | { 11 | 12 | bool g_isDrained = false; 13 | std::vector g_messages; 14 | 15 | } // namespace 16 | 17 | namespace 18 | { 19 | 20 | void Log( const std::string& msg, bool addComponentName, bool addNewLine ) 21 | { 22 | const auto formattedMsg = ( addComponentName ? DNET_UNDERSCORE_NAME ":\n" : "" ) + msg + ( addNewLine ? "\n" : "" ); 23 | if ( !g_isDrained ) 24 | { 25 | g_messages.emplace_back( formattedMsg ); 26 | } 27 | else 28 | { 29 | FB2K_console_formatter() << formattedMsg.c_str(); 30 | } 31 | } 32 | 33 | void Drain() 34 | { 35 | for ( const auto& message: g_messages ) 36 | { 37 | FB2K_console_formatter() << message.c_str(); 38 | } 39 | g_messages.clear(); 40 | g_isDrained = true; 41 | } 42 | 43 | } // namespace 44 | 45 | #pragma managed 46 | 47 | namespace Qwr::DotnetHost 48 | { 49 | 50 | void DelayedLog( String ^ text, bool addComponentName, bool addNewLine ) 51 | { 52 | Log( Convert::ToNative::ToValue( text ), addComponentName, addNewLine ); 53 | } 54 | 55 | void DrainDelayedLog() 56 | { 57 | Drain(); 58 | } 59 | 60 | } // namespace Qwr::DotnetHost 61 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/utils/delayed_log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | 5 | namespace Qwr::DotnetHost 6 | { 7 | 8 | /// @brief This should be used in situations where console logger might not be ready yet 9 | void DelayedLog( String ^ text, bool addComponentName = true, bool addNewLine = true ); 10 | 11 | void DrainDelayedLog(); 12 | 13 | } // namespace Qwr::DotnetHost 14 | -------------------------------------------------------------------------------- /foo_dotnet_component_host/utils/menu_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Qwr::DotnetHost 4 | { 5 | 6 | void ExecuteContextMenuCommand( const std::string& name, const metadb_handle_list& p_handles, uint32_t flags ); 7 | void ExecuteMainMenuCommand( const std::string& name ); 8 | 9 | } // namespace smp::utils 10 | -------------------------------------------------------------------------------- /licenses/JSON for Modern C++.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2018 Niels Lohmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /licenses/PFC.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2002-2019 Peter Pawlowski 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /licenses/fmt.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - present, Victor Zverovich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- Optional exception to the license --- 23 | 24 | As an exception, if, as a result of your compiling your source code, portions 25 | of this Software are embedded into a machine-executable object form of such 26 | source code, you may redistribute such embedded portions in such object form 27 | without including the above copyright and permission notices. 28 | -------------------------------------------------------------------------------- /licenses/foobar2000 SDK.txt: -------------------------------------------------------------------------------- 1 | foobar2000 1.5 SDK 2 | Copyright (c) 2001-2019, Peter Pawlowski 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | 12 | Note that separate less restrictive licenses apply to the included 'pfc' and 'libPPUI' libraries. See license files included in respective folders for details. 13 | -------------------------------------------------------------------------------- /props/submodules/fb2k_utils.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | <_PropertySheetDisplayName>fb2k_utils 7 | 8 | 9 | $(SolutionDir)..\submodules\fb2k_utils\ 10 | $(QwrFb2kUtilsRootDir)props\ 11 | 12 | 13 | $(QwrFb2kUtilsRootDir)src\;$(IncludePath) 14 | 15 | -------------------------------------------------------------------------------- /props/submodules/submodules.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | <_PropertySheetDisplayName>submodules 7 | 8 | 9 | $(SolutionDir)..\submodules\ 10 | 11 | 12 | $(SubmodulesDirectory);$(IncludePath) 13 | 14 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | ### Main scripts 2 | - setup.py - Set up everything, so that project can be built. 3 | - pack_component.py - Pack project binaries to .fb2k-component archive. 4 | - generate_docs.py - Generate HTML docs from JsDoc docs. 5 | 6 | ### Auxiliary scripts 7 | - update_submodules.py - Update submodules to their latest version. 8 | - update_gh_pages.py - Update gh-pages folder with latest generated content. 9 | -------------------------------------------------------------------------------- /scripts/call_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import traceback 4 | import sys 5 | 6 | class SkippedError(Exception): 7 | def __init__(self): 8 | self.message = "Skipped error" 9 | 10 | def final_call_decorator(start_msg: str, 11 | success_msg: str, 12 | failure_msg: str): 13 | def f_decorator(f): 14 | def wrapper(*args, **kwds): 15 | print(start_msg) 16 | try: 17 | f(*args, **kwds) 18 | print(success_msg) 19 | sys.exit(0) 20 | except SkippedError: 21 | print(failure_msg, file=sys.stderr) 22 | sys.exit(1) 23 | except Exception: 24 | traceback.print_exc(file=sys.stderr) 25 | print(failure_msg, file=sys.stderr) 26 | sys.exit(1) 27 | 28 | return wrapper 29 | 30 | return f_decorator 31 | -------------------------------------------------------------------------------- /scripts/download_submodules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | from pathlib import Path 5 | 6 | import call_wrapper 7 | 8 | def download_submodule(root_dir, submodule_name): 9 | print(f"Downloading {submodule_name}...") 10 | try: 11 | subprocess.check_call(f"git submodule update --init --depth=10 -- submodules/{submodule_name}", cwd=root_dir, shell=True) 12 | except subprocess.CalledProcessError: 13 | try: 14 | subprocess.check_call(f"git submodule update --init --depth=50 -- submodules/{submodule_name}", cwd=root_dir, shell=True) 15 | except subprocess.CalledProcessError: 16 | # Shallow copy does not honour default branch config 17 | subprocess.check_call("git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/*", 18 | cwd=root_dir/"submodules"/submodule_name, 19 | shell=True) 20 | subprocess.check_call(f"git submodule deinit --force -- submodules/{submodule_name}", cwd=root_dir, shell=True) 21 | subprocess.check_call(f"git submodule update --init --force -- submodules/{submodule_name}", cwd=root_dir, shell=True) 22 | 23 | def download(): 24 | cur_dir = Path(__file__).parent.absolute() 25 | root_dir = cur_dir.parent 26 | 27 | subprocess.check_call("git submodule sync", cwd=root_dir, shell=True) 28 | subprocess.check_call("git submodule foreach git reset --hard", cwd=root_dir, shell=True) 29 | for subdir in [f for f in (root_dir/"submodules").iterdir() if f.is_dir()]: 30 | download_submodule(root_dir, subdir.name) 31 | 32 | if __name__ == '__main__': 33 | call_wrapper.final_call_decorator( 34 | "Downloading submodules", 35 | "Downloading submodules: success", 36 | "Downloading submodules: failure!" 37 | )(download)() 38 | -------------------------------------------------------------------------------- /scripts/generate_package_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | from pathlib import Path 6 | 7 | import call_wrapper 8 | 9 | def generate(): 10 | cur_dir = Path(__file__).parent.absolute() 11 | root_dir = cur_dir.parent 12 | output_dir = root_dir/"_result"/"AllPlatforms"/"generated" 13 | output_dir.mkdir(parents=True, exist_ok=True) 14 | 15 | output_file = output_dir/'doc_package_info.json' 16 | output_file.unlink(missing_ok=True) 17 | 18 | tag = subprocess.check_output("git describe --tags", shell=True).decode('ascii').strip() 19 | if (tag.startswith('v')): 20 | # JsDoc template adds `v` to the version, hence the removal 21 | tag = tag[1:] 22 | 23 | data = { 24 | "name": "Spider Monkey Panel", 25 | "version": tag 26 | } 27 | 28 | with open(output_file, 'w') as output: 29 | json.dump(data, output) 30 | 31 | print(f"Generated file: {output_file}") 32 | 33 | if __name__ == '__main__': 34 | call_wrapper.final_call_decorator( 35 | "Generating package info for docs", 36 | "Generating package info: success", 37 | "Generating package info: failure!" 38 | )(generate)() 39 | -------------------------------------------------------------------------------- /scripts/pack_component.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import zipfile 5 | from pathlib import Path 6 | from zipfile import ZipFile 7 | 8 | import call_wrapper 9 | 10 | def path_basename_tuple(path): 11 | return (path, path.name) 12 | 13 | def zipdir(zip_file, path, arc_path=None): 14 | assert(path.exists() and path.is_dir()) 15 | 16 | for file in path.rglob("*"): 17 | if (file.name.startswith(".")): 18 | # skip `hidden` files 19 | continue 20 | if (arc_path): 21 | file_arc_path = f"{arc_path}/{file.relative_to(path)}" 22 | else: 23 | file_arc_path = file.relative_to(path) 24 | zip_file.write(file, file_arc_path) 25 | 26 | def pack(is_debug = False): 27 | cur_dir = Path(__file__).parent.absolute() 28 | root_dir = cur_dir.parent 29 | result_machine_dir = root_dir/"_result"/("Win32_Debug" if is_debug else "Win32_Release") 30 | assert(result_machine_dir.exists() and result_machine_dir.is_dir()) 31 | result_any_machine_dir = root_dir/"_result"/("AnyCPU_Debug" if is_debug else "AnyCPU_Release") 32 | assert(result_any_machine_dir.exists() and result_any_machine_dir.is_dir()) 33 | 34 | output_dir = result_machine_dir 35 | output_dir.mkdir(parents=True, exist_ok=True) 36 | 37 | component_zip = output_dir/"foo_dotnet_component_host.fb2k-component" 38 | component_zip.unlink(missing_ok=True) 39 | 40 | with ZipFile(component_zip, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as z: 41 | zipdir(z, root_dir/"licenses", "licenses") 42 | z.write(*path_basename_tuple(root_dir/"LICENSE")) 43 | z.write(*path_basename_tuple(root_dir/"CHANGELOG.md")) 44 | 45 | files_to_pack = [ 46 | 'Ijwhost.dll', 47 | 'foo_dotnet_component_host.deps.json', 48 | 'foo_dotnet_component_host.dll', 49 | 'foo_dotnet_component_host.runtimeconfig.json' 50 | ] 51 | files_to_pack_any = [ 52 | 'dotnet_component_interface.deps.json', 53 | 'dotnet_component_interface.dll' 54 | ] 55 | 56 | for f in files_to_pack: 57 | z.write(*path_basename_tuple(result_machine_dir/'bin'/f)) 58 | for f in files_to_pack_any: 59 | z.write(*path_basename_tuple(result_any_machine_dir/'bin'/f)) 60 | 61 | print(f"Generated file: {component_zip}") 62 | 63 | if (not is_debug): 64 | # Release pdbs are packed in a separate package 65 | pdb_zip = output_dir/"foo_dotnet_component_host_pdb.zip" 66 | if (pdb_zip.exists()): 67 | pdb_zip.unlink() 68 | 69 | with ZipFile(pdb_zip, "w", zipfile.ZIP_DEFLATED) as z: 70 | z.write(*path_basename_tuple(result_any_machine_dir/"bin"/"dotnet_component_interface.pdb")) 71 | z.write(*path_basename_tuple(result_machine_dir/"dbginfo"/"foo_dotnet_component_host.pdb")) 72 | 73 | print(f"Generated file: {pdb_zip}") 74 | 75 | if __name__ == '__main__': 76 | parser = argparse.ArgumentParser(description='Pack component to .fb2k-component') 77 | parser.add_argument('--debug', default=False, action='store_true') 78 | 79 | args = parser.parse_args() 80 | 81 | call_wrapper.final_call_decorator( 82 | "Packing component", 83 | "Packing component: success", 84 | "Packing component: failure!" 85 | )( 86 | pack 87 | )( 88 | args.debug 89 | ) 90 | -------------------------------------------------------------------------------- /scripts/patch_submodules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | from pathlib import Path 5 | 6 | import call_wrapper 7 | 8 | def patch(): 9 | cur_dir = Path(__file__).parent.absolute() 10 | root_dir = cur_dir.parent 11 | patches = [cur_dir/"patches"/p for p in ["foobar2000.patch", "pfc.patch"]] 12 | for p in patches: 13 | assert(p.exists() and p.is_file()) 14 | 15 | subprocess.check_call(f"git apply --ignore-whitespace {' '.join(str(p) for p in patches)}", cwd=root_dir, shell=True) 16 | 17 | if __name__ == '__main__': 18 | call_wrapper.final_call_decorator( 19 | "Patching submodules", 20 | "Patching submodules: success", 21 | "Patching submodules: failure!" 22 | )(patch)() 23 | -------------------------------------------------------------------------------- /scripts/patches/foobar2000.patch: -------------------------------------------------------------------------------- 1 | diff --git a/submodules/foobar2000/SDK/foobar2000_SDK.vcxproj b/submodules/foobar2000/SDK/foobar2000_SDK.vcxproj 2 | index 1772538..4948235 100644 3 | --- a/submodules/foobar2000/SDK/foobar2000_SDK.vcxproj 4 | +++ b/submodules/foobar2000/SDK/foobar2000_SDK.vcxproj 5 | @@ -50,7 +55,7 @@ 6 | Level3 7 | true 8 | ProgramDatabase 9 | - MultiThreadedDebug 10 | + MultiThreadedDebugDLL 11 | 4715 12 | true 13 | 14 | @@ -74,7 +79,7 @@ 15 | Level3 16 | true 17 | ProgramDatabase 18 | - MultiThreaded 19 | + MultiThreadedDLL 20 | /d2notypeopt %(AdditionalOptions) 21 | 4715 22 | true 23 | -------------------------------------------------------------------------------- /scripts/patches/pfc.patch: -------------------------------------------------------------------------------- 1 | diff --git a/submodules/pfc/pfc.vcxproj b/submodules/pfc/pfc.vcxproj 2 | index ff07a40..1b58a9e 100644 3 | --- a/submodules/pfc/pfc.vcxproj 4 | +++ b/submodules/pfc/pfc.vcxproj 5 | @@ -132,7 +126,7 @@ 6 | Level3 7 | true 8 | ProgramDatabase 9 | - MultiThreadedDebug 10 | + MultiThreadedDebugDLL 11 | 4715 12 | true 13 | false 14 | @@ -155,7 +149,7 @@ 15 | Level3 16 | true 17 | ProgramDatabase 18 | - MultiThreadedDebug 19 | + MultiThreadedDebugDLL 20 | 4715 21 | true 22 | false 23 | @@ -178,7 +172,7 @@ 24 | Level3 25 | true 26 | ProgramDatabase 27 | - MultiThreadedDebug 28 | + MultiThreadedDebugDLL 29 | 4715 30 | true 31 | false 32 | @@ -200,7 +194,7 @@ 33 | Level3 34 | true 35 | ProgramDatabase 36 | - MultiThreadedDebug 37 | + MultiThreadedDebugDLL 38 | 4715 39 | true 40 | false 41 | @@ -225,7 +219,7 @@ 42 | Level3 43 | true 44 | ProgramDatabase 45 | - MultiThreaded 46 | + MultiThreadedDLL 47 | /d2notypeopt %(AdditionalOptions) 48 | 4715 49 | true 50 | @@ -251,7 +246,7 @@ 51 | Level3 52 | true 53 | ProgramDatabase 54 | - MultiThreaded 55 | + MultiThreadedDLL 56 | /d2notypeopt %(AdditionalOptions) 57 | 4715 58 | true 59 | @@ -277,7 +273,7 @@ 60 | Level3 61 | true 62 | ProgramDatabase 63 | - MultiThreaded 64 | + MultiThreadedDLL 65 | /d2notypeopt %(AdditionalOptions) 66 | 4715 67 | true 68 | @@ -304,7 +300,7 @@ 69 | Level3 70 | true 71 | ProgramDatabase 72 | - MultiThreaded 73 | + MultiThreadedDLL 74 | /d2notypeopt %(AdditionalOptions) 75 | 4715 76 | true 77 | -------------------------------------------------------------------------------- /scripts/publish_interface_package.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import glob 5 | import subprocess 6 | from pathlib import Path 7 | 8 | import call_wrapper 9 | 10 | def generate(is_debug = False): 11 | cur_dir = Path(__file__).parent.absolute() 12 | root_dir = cur_dir.parent 13 | 14 | result_any_machine_dir = root_dir/'_result'/('AnyCPU_Debug' if is_debug else 'AnyCPU_Release') 15 | assert result_any_machine_dir.exists() and result_any_machine_dir.is_dir() 16 | 17 | package_files = glob.glob(str(result_any_machine_dir/'bin'/'dotnet_component_interface*.nupkg')) 18 | assert len(package_files) == 1 19 | 20 | package_path = package_files[0] 21 | 22 | subprocess.check_call(['dotnet', 'nuget', 23 | 'push', 24 | str(package_path), 25 | '--source', 'github'], 26 | cwd=root_dir, shell=True) 27 | print(f'Package published') 28 | 29 | if __name__ == '__main__': 30 | parser = argparse.ArgumentParser(description='Publish interface package to GitHub') 31 | parser.add_argument('--debug', default=False, action='store_true') 32 | 33 | args = parser.parse_args() 34 | 35 | call_wrapper.final_call_decorator( 36 | 'Publishing interface package', 37 | 'Publishing interface package: success', 38 | 'Publishing interface package: failure!' 39 | )( 40 | generate 41 | )( 42 | args.debug 43 | ) 44 | -------------------------------------------------------------------------------- /scripts/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import importlib 5 | import importlib.util 6 | import sys 7 | import traceback 8 | from pathlib import Path 9 | from typing import Union 10 | 11 | import call_wrapper 12 | import download_submodules 13 | import patch_submodules 14 | 15 | PathLike = Union[str, Path] 16 | 17 | def call_decorator(command_name: str): 18 | def f_decorator(f): 19 | def wrapper(*args, **kwds): 20 | print(f">> {command_name}: starting") 21 | try: 22 | f(*args, **kwds) 23 | print(f"<< {command_name}: success") 24 | except Exception: 25 | traceback.print_exc(file=sys.stderr) 26 | print(f"<< {command_name}: failure", file=sys.stderr) 27 | raise call_wrapper.SkippedError() 28 | 29 | return wrapper 30 | 31 | return f_decorator 32 | 33 | def load_module(script_path): 34 | spec = importlib.util.spec_from_file_location("module.name", script_path) 35 | mod = importlib.util.module_from_spec(spec) 36 | spec.loader.exec_module(mod) 37 | return mod 38 | 39 | def setup( skip_submodules_download, 40 | skip_submodules_patches ): 41 | cur_dir = Path(__file__).parent.absolute() 42 | root_dir = cur_dir.parent 43 | scripts_path = root_dir/'submodules'/'fb2k_utils'/'scripts' 44 | 45 | if (not skip_submodules_download): 46 | call_decorator('Downloading submodules')(download_submodules.download)() 47 | if (not skip_submodules_patches): 48 | call_decorator('Patching fb2k submodules')( 49 | load_module(scripts_path/'patch_fb2k_submodules.py').patch 50 | )( 51 | root_dir=root_dir 52 | ) 53 | call_decorator("Patching submodules")(patch_submodules.patch)() 54 | 55 | call_decorator('Version header generation')( 56 | load_module(scripts_path/'generate_version_header.py').generate_header_custom 57 | )( 58 | repo_dir=root_dir, 59 | output_dir=root_dir/'_result'/'AllPlatforms'/'generated', 60 | component_prefix='DNET' 61 | ) 62 | call_decorator('Commit hash header generation')( 63 | load_module(scripts_path/'generate_commit_hash_header.py').generate_header_custom 64 | )( 65 | repo_dir=root_dir, 66 | output_dir=root_dir/'_result'/'AllPlatforms'/'generated', 67 | component_prefix='DNET' 68 | ) 69 | call_decorator('SourceLink configuration file generation' 70 | )( 71 | load_module(scripts_path/'generate_source_link_config.py').generate_config_custom 72 | )( 73 | repo_dir=root_dir, 74 | output_dir=root_dir/'_result'/'AllPlatforms'/'generated', 75 | repo='theqwertiest/foo_dotnet_component_host' 76 | ) 77 | call_decorator('3rd-party notices generation' 78 | )( 79 | load_module(scripts_path/'generate_third_party.py').generate 80 | )( 81 | root_dir=root_dir, 82 | component_name='Spider Monkey Panel' 83 | ) 84 | 85 | if __name__ == '__main__': 86 | parser = argparse.ArgumentParser(description='Setup project') 87 | parser.add_argument('--skip_submodules_download', default=False, action='store_true') 88 | parser.add_argument('--skip_submodules_patches', default=False, action='store_true') 89 | 90 | args = parser.parse_args() 91 | 92 | call_wrapper.final_call_decorator( 93 | "Preparing project repo", 94 | "Setup complete!", 95 | "Setup failed!" 96 | )( 97 | setup 98 | )( 99 | args.skip_submodules_download, 100 | args.skip_submodules_patches 101 | ) 102 | -------------------------------------------------------------------------------- /scripts/update_gh_pages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import shutil 5 | from pathlib import Path 6 | from typing import Union 7 | 8 | import call_wrapper 9 | 10 | PathLike = Union[str, Path] 11 | 12 | def update(gh_pages_dir: PathLike): 13 | cur_dir = Path(__file__).parent.absolute() 14 | root_dir = cur_dir.parent 15 | 16 | gh_pages_dir = Path(gh_pages_dir).resolve() 17 | assert(gh_pages_dir.exists() and gh_pages_dir.is_dir()) 18 | 19 | ghp_gen_dir = gh_pages_dir/"assets"/"generated_files" 20 | if (ghp_gen_dir.exists()): 21 | shutil.rmtree(ghp_gen_dir) 22 | ghp_gen_dir.mkdir(parents=True) 23 | 24 | shutil.copytree(root_dir/"licenses", ghp_gen_dir/"licenses", dirs_exist_ok=True) 25 | shutil.copy2(root_dir/".license_index.txt", ghp_gen_dir/"licenses") 26 | 27 | shutil.copy2(root_dir/"CHANGELOG.md", gh_pages_dir/"_includes"/"CHANGELOG.md") 28 | 29 | if __name__ == '__main__': 30 | cur_dir = Path(__file__).parent.absolute() 31 | root_dir = cur_dir.parent 32 | gh_pages_dir = root_dir/"gh-pages" 33 | 34 | parser = argparse.ArgumentParser(description='Update GitHub Pages content') 35 | parser.add_argument('--gh_pages_dir', default=gh_pages_dir) 36 | 37 | args = parser.parse_args() 38 | 39 | call_wrapper.final_call_decorator( 40 | "Updating GitHub Pages content", 41 | "Updating GitHub Pages content: success", 42 | "Updating GitHub Pages content: failure!" 43 | )( 44 | update 45 | )( 46 | gh_pages_dir 47 | ) 48 | -------------------------------------------------------------------------------- /workspaces/foo_dotnet_component_host.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31624.102 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfc", "..\submodules\pfc\pfc.vcxproj", "{EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "foobar2000_SDK", "..\submodules\foobar2000\SDK\foobar2000_SDK.vcxproj", "{E8091321-D79D-4575-86EF-064EA1A4A20D}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "submodules", "submodules", "{3F23C685-CB17-4474-A100-C18FAA04D90C}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "foo_dotnet_component_host", "..\foo_dotnet_component_host\foo_dotnet_component_host.vcxproj", "{D44C2E87-896A-4F82-AF57-8FFB76D93287}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EF8432DB-C427-4A6D-954C-9B31B2A4BFE4}" 15 | ProjectSection(SolutionItems) = preProject 16 | ..\.editorconfig = ..\.editorconfig 17 | ..\CHANGELOG.md = ..\CHANGELOG.md 18 | ..\README.md = ..\README.md 19 | EndProjectSection 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet_component_interface", "..\dotnet_component_interface\dotnet_component_interface.csproj", "{18FFD467-1A00-4EEF-8229-A29809F8CA89}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet_test", "..\dotnet_test\dotnet_test.csproj", "{2F9C5AEA-9786-47D9-9700-169056B64DEC}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Win32 = Debug|Win32 28 | Release|Win32 = Release|Win32 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Debug|Win32.ActiveCfg = Debug|Win32 32 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Debug|Win32.Build.0 = Debug|Win32 33 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Release|Win32.ActiveCfg = Release|Win32 34 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Release|Win32.Build.0 = Release|Win32 35 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Debug|Win32.ActiveCfg = Debug|Win32 36 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Debug|Win32.Build.0 = Debug|Win32 37 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Release|Win32.ActiveCfg = Release|Win32 38 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Release|Win32.Build.0 = Release|Win32 39 | {D44C2E87-896A-4F82-AF57-8FFB76D93287}.Debug|Win32.ActiveCfg = Debug|Win32 40 | {D44C2E87-896A-4F82-AF57-8FFB76D93287}.Debug|Win32.Build.0 = Debug|Win32 41 | {D44C2E87-896A-4F82-AF57-8FFB76D93287}.Release|Win32.ActiveCfg = Release|Win32 42 | {D44C2E87-896A-4F82-AF57-8FFB76D93287}.Release|Win32.Build.0 = Release|Win32 43 | {18FFD467-1A00-4EEF-8229-A29809F8CA89}.Debug|Win32.ActiveCfg = Debug|Any CPU 44 | {18FFD467-1A00-4EEF-8229-A29809F8CA89}.Debug|Win32.Build.0 = Debug|Any CPU 45 | {18FFD467-1A00-4EEF-8229-A29809F8CA89}.Release|Win32.ActiveCfg = Release|Any CPU 46 | {18FFD467-1A00-4EEF-8229-A29809F8CA89}.Release|Win32.Build.0 = Release|Any CPU 47 | {2F9C5AEA-9786-47D9-9700-169056B64DEC}.Debug|Win32.ActiveCfg = Debug|Any CPU 48 | {2F9C5AEA-9786-47D9-9700-169056B64DEC}.Debug|Win32.Build.0 = Debug|Any CPU 49 | {2F9C5AEA-9786-47D9-9700-169056B64DEC}.Release|Win32.ActiveCfg = Release|Any CPU 50 | {2F9C5AEA-9786-47D9-9700-169056B64DEC}.Release|Win32.Build.0 = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(NestedProjects) = preSolution 56 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C} = {3F23C685-CB17-4474-A100-C18FAA04D90C} 57 | {E8091321-D79D-4575-86EF-064EA1A4A20D} = {3F23C685-CB17-4474-A100-C18FAA04D90C} 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {9500DFB4-B9CD-479C-BFC0-A3828AC18AB9} 61 | EndGlobalSection 62 | EndGlobal 63 | --------------------------------------------------------------------------------