├── .github └── workflows │ ├── codeql.yml │ └── main.yml ├── .gitignore ├── ArtifactBuild.cmd ├── Build.cmd ├── BuildInstaller.bat ├── CONTRIBUTING.md ├── LICENSE ├── Paths.cmd.template ├── README.md ├── UnityLauncherPro.sln ├── UnityLauncherPro ├── App.config ├── App.xaml ├── App.xaml.cs ├── Converters │ ├── LastModifiedConverter.cs │ └── ReleaseDateConverter.cs ├── Data │ ├── BuildReport.cs │ ├── BuildReportItem.cs │ ├── DownloadProgress.cs │ ├── MessageType.cs │ ├── Platform.cs │ ├── Project.cs │ ├── Tabs.cs │ ├── ThemeColor.cs │ ├── UnityInstallation.cs │ ├── UnityVersion.cs │ ├── UnityVersionResponse.cs │ └── UnityVersionStream.cs ├── DownloadProgressWindow.xaml ├── DownloadProgressWindow.xaml.cs ├── GetProjects.cs ├── GetUnityInstallations.cs ├── GetUnityUpdates.cs ├── Helpers │ ├── ObservableDictionary.cs │ └── ProcessHandler.cs ├── Images │ ├── icon.ico │ └── icon.png ├── Libraries │ └── ExtractTarGz.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── NewProject.xaml ├── NewProject.xaml.cs ├── ProjectProperties.xaml ├── ProjectProperties.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── Colors.xaml │ └── UnityVersionCache.json ├── ThemeEditor.xaml ├── ThemeEditor.xaml.cs ├── Tools.cs ├── UnityLauncherPro.csproj ├── UpgradeWindow.xaml ├── UpgradeWindow.xaml.cs ├── Version.cs └── app.manifest ├── UnityLauncherProInstaller └── UnityLauncherProInstaller.vdproj └── appveyor.yml /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '19 2 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: windows-2019 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'csharp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ master ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | jobs: 13 | # This workflow contains a single job called "build" 14 | build: 15 | # The type of runner that the job will run on 16 | runs-on: windows-2022 17 | 18 | # Steps represent a sequence of tasks that will be executed as part of the job 19 | steps: 20 | # Step to check if the commit message contains #GITBUILD 21 | - name: Check Commit Message 22 | shell: powershell 23 | run: | 24 | # Get the commit message 25 | $strVal = '${{ github.event.commits[0].message }}' 26 | # Convert commit message to a single line if multiline 27 | $singleLineStrVal = $strVal -replace "`r`n", " " -replace "`n", " " 28 | if ($singleLineStrVal -match '#GITBUILD') { 29 | Write-Host 'Build commit detected. Proceeding with build...' 30 | echo "build_trigger=true" >> $env:GITHUB_ENV 31 | } else { 32 | Write-Host 'No build commit. Skipping build steps...' 33 | echo "build_trigger=false" >> $env:GITHUB_ENV 34 | } 35 | 36 | # Step to ensure the repository is checked out 37 | - uses: actions/checkout@v2 38 | 39 | # Inform if build steps are skipped 40 | - name: Inform Skipped Build Steps 41 | if: env.build_trigger != 'true' 42 | shell: powershell 43 | run: | 44 | Write-Host "Skipping build steps because the commit message does not contain #GITBUILD." 45 | 46 | # Install 7Zip PowerShell module 47 | - name: Install 7Zip PowerShell Module 48 | if: env.build_trigger == 'true' 49 | shell: powershell 50 | run: Install-Module 7Zip4PowerShell -Force -Verbose 51 | 52 | # Restore NuGet packages 53 | - name: Restore NuGet packages 54 | if: env.build_trigger == 'true' 55 | run: nuget restore UnityLauncherPro.sln 56 | 57 | # Build the binary 58 | - name: Build Binary 59 | if: env.build_trigger == 'true' 60 | shell: cmd 61 | run: call .\Build.cmd 62 | 63 | # Build the artifact 64 | - name: Build Artifact 65 | if: env.build_trigger == 'true' 66 | shell: cmd 67 | run: call .\ArtifactBuild.cmd 68 | 69 | # Check that output exists 70 | - name: Validate UnityLauncherPro.exe exists 71 | if: env.build_trigger == 'true' 72 | shell: cmd 73 | run: | 74 | if not exist "UnityLauncherPro\bin\Release\UnityLauncherPro.exe" ( 75 | echo ERROR: UnityLauncherPro.exe not found. 76 | exit /b 1 77 | ) 78 | echo Found UnityLauncherPro.exe 79 | 80 | # 1) Compute a wrapped major.minor.build from the run number 81 | - name: Compute installer version 82 | if: env.build_trigger == 'true' 83 | shell: bash 84 | run: | 85 | TOTAL=${{ github.run_number }} 86 | BUILD=$(( TOTAL % 65536 )) 87 | MINOR_TOTAL=$(( TOTAL / 65536 )) 88 | MINOR=$(( MINOR_TOTAL % 256 )) 89 | MAJOR=$(( MINOR_TOTAL / 256 )) 90 | echo "INSTALLER_MAJOR=$MAJOR" >> $GITHUB_ENV 91 | echo "INSTALLER_MINOR=$MINOR" >> $GITHUB_ENV 92 | echo "INSTALLER_BUILD=$BUILD" >> $GITHUB_ENV 93 | echo "INSTALLER_VER=$MAJOR.$MINOR.$BUILD" >> $GITHUB_ENV 94 | echo "Computed installer version → $MAJOR.$MINOR.$BUILD (run #${{ github.run_number }})" 95 | 96 | # 2) Patch your .vdproj in place (inject ProductVersion and a new GUID) 97 | - name: Patch .vdproj ProductVersion & ProductCode 98 | if: env.build_trigger == 'true' 99 | shell: pwsh 100 | run: | 101 | $proj = 'UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj' 102 | $ver = "${{ env.INSTALLER_VER }}" # e.g. 0.0.118 103 | $guid = [Guid]::NewGuid().ToString("B").ToUpper() # e.g. {E821A3F5-1F84-4A4B-BE9D-115D93E9E6F0} 104 | 105 | # Read & rewrite line-by-line 106 | $fixed = Get-Content $proj | ForEach-Object { 107 | if ($_ -match '^(\s*)"ProductVersion"') { 108 | # preserve indentation, then inject exactly one pair of braces 109 | "$($Matches[1])""ProductVersion"" = ""8:$ver""" 110 | } 111 | elseif ($_ -match '^(\s*)"ProductCode"') { 112 | "$($Matches[1])""ProductCode"" = ""8:$guid""" 113 | } 114 | else { 115 | $_ 116 | } 117 | } 118 | 119 | # Overwrite the project 120 | Set-Content -Path $proj -Value $fixed 121 | 122 | Write-Host "→ ProductVersion patched to 8:$ver" 123 | Write-Host "→ ProductCode patched to 8:$guid" 124 | 125 | 126 | # 3) **DEBUG**: print out the patched .vdproj so you can inspect it 127 | - name: Show patched .vdproj 128 | if: env.build_trigger == 'true' 129 | shell: pwsh 130 | run: | 131 | $proj = 'UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj' 132 | Write-Host "=== BEGIN .vdproj CONTENT ===" 133 | Get-Content $proj 134 | Write-Host "=== END .vdproj CONTENT ===" 135 | 136 | # locate VS 2022 137 | - name: Locate Visual Studio 2022 138 | id: vswhere 139 | shell: pwsh 140 | run: | 141 | $installPath = vswhere -latest -products * -requires Microsoft.Component.MSBuild ` 142 | -property installationPath 143 | if (-not $installPath) { throw 'VS 2022 not found' } 144 | Write-Host "##[set-output name=installPath]$installPath" 145 | 146 | # download vs installer builder (v2.0.1) 147 | - name: Download Installer-Projects VSIX 148 | shell: pwsh 149 | run: | 150 | $vsixUrl = "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioClient/vsextensions/MicrosoftVisualStudio2022InstallerProjects/2.0.1/vspackage" 151 | Invoke-WebRequest $vsixUrl -OutFile installerprojects.vsix 152 | 153 | # install vs installer builder 154 | - name: Install Installer-Projects extension 155 | shell: pwsh 156 | run: | 157 | $vsixInstaller = Join-Path "${{ steps.vswhere.outputs.installPath }}" 'Common7\IDE\VSIXInstaller.exe' 158 | Write-Host "Running: $vsixInstaller installerprojects.vsix /quiet" 159 | & $vsixInstaller installerprojects.vsix /quiet 160 | 161 | # Build MSI installer project using Visual Studio 2022 workaround 162 | - name: Build Installer MSI 163 | if: env.build_trigger == 'true' 164 | shell: cmd 165 | run: | 166 | echo === Running BuildInstaller.bat === 167 | call .\BuildInstaller.bat || echo WARNING: BuildInstaller.bat exited with %ERRORLEVEL%, continuing... 168 | echo === Verifying MSI === 169 | if exist "UnityLauncherProInstaller\Release\UnityLauncherPro-Installer.msi" ( 170 | echo Success: MSI found at UnityLauncherProInstaller\Release\UnityLauncherPro-Installer.msi 171 | exit /b 0 172 | ) else ( 173 | echo ERROR: MSI not found in UnityLauncherProInstaller\Release 174 | exit /b 1 175 | ) 176 | 177 | # Get the current date and time 178 | - name: Get current date and time 179 | id: datetime 180 | if: env.build_trigger == 'true' # Only run if build was triggered 181 | run: | 182 | # Save the current date and time to an environment variable 183 | echo "current_datetime=$(date +'%d/%m/%Y %H:%M')" >> $GITHUB_ENV 184 | 185 | # Step to get previous tag and commits 186 | - name: Get commits since last release 187 | id: get_commits 188 | if: env.build_trigger == 'true' # Only run if build was triggered 189 | shell: bash 190 | run: | 191 | # Get the most recent tag 192 | PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "none") 193 | if [ "$PREV_TAG" = "none" ]; then 194 | echo "No previous tag found, listing all commits" 195 | COMMITS=$(git log --pretty=format:"* %s" --no-merges) 196 | else 197 | echo "Previous tag: $PREV_TAG" 198 | # List commits since last tag 199 | COMMITS=$(git log $PREV_TAG..HEAD --pretty=format:"* %s" --no-merges) 200 | fi 201 | # Save commits to the environment 202 | echo "commits=$COMMITS" >> $GITHUB_ENV 203 | 204 | # Create a release 205 | - name: Create Release 206 | id: create_release 207 | if: env.build_trigger == 'true' # Execute only if build was triggered 208 | uses: actions/create-release@latest 209 | env: 210 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 211 | with: 212 | tag_name: ${{github.run_number}} 213 | release_name: ${{ env.current_datetime }} (${{ github.run_number }}) 214 | body: | 215 | Automated Release by GitHub Action CI 216 | 217 | ### Commits in this release: 218 | ${{ env.commits }} 219 | draft: false 220 | prerelease: false 221 | 222 | # Upload the release asset 223 | - name: Upload Release Asset 224 | id: upload-release-asset 225 | if: env.build_trigger == 'true' # Execute only if build was triggered 226 | uses: actions/upload-release-asset@v1 227 | env: 228 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 229 | with: 230 | upload_url: ${{ steps.create_release.outputs.upload_url }} 231 | asset_path: ./UnityLauncherPro.zip 232 | asset_name: UnityLauncherPro.zip 233 | asset_content_type: application/zip 234 | 235 | # Upload MSI installer to release 236 | - name: Upload MSI Installer 237 | if: env.build_trigger == 'true' 238 | uses: actions/upload-release-asset@v1 239 | env: 240 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 241 | with: 242 | upload_url: ${{ steps.create_release.outputs.upload_url }} 243 | asset_path: ./UnityLauncherProInstaller/Release/UnityLauncherPro-Installer.msi 244 | asset_name: UnityLauncherPro-Installer.msi 245 | asset_content_type: application/x-msi 246 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | /Paths.cmd 332 | -------------------------------------------------------------------------------- /ArtifactBuild.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd "%~dp0" 3 | powershell Compress-7Zip "UnityLauncherPro\bin\Release\UnityLauncherPro.exe" -ArchiveFileName "UnityLauncherPro.zip" -Format Zip 4 | :exit 5 | popd 6 | @echo on 7 | -------------------------------------------------------------------------------- /Build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Default VS paths to check if no Paths.cmd file exists 4 | set VISUAL_STUDIO_PATH_0="%programfiles%\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\msbuild.exe" 5 | set VISUAL_STUDIO_PATH_1="%programfiles%\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\msbuild.exe" 6 | set VISUAL_STUDIO_PATH_2="%programfiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" 7 | set VISUAL_STUDIO_PATH_3="%programfiles(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" 8 | 9 | pushd "%~dp0" 10 | if exist Debug rd /s /q Debug 11 | if exist Release rd /s /q Release 12 | if exist x64 rd /s /q x64 13 | 14 | if exist "Paths.cmd" ( 15 | REM Prefer Paths.cmd as Visual Studio path source if it exists. 16 | call Paths.cmd 17 | goto build 18 | ) else ( 19 | REM Otherwise try to auto-detect the Visual Studio path. 20 | if exist %VISUAL_STUDIO_PATH_0% ( 21 | set VISUAL_STUDIO_PATH=%VISUAL_STUDIO_PATH_0% 22 | goto build 23 | ) 24 | 25 | if exist %VISUAL_STUDIO_PATH_1% ( 26 | set VISUAL_STUDIO_PATH=%VISUAL_STUDIO_PATH_1% 27 | goto build 28 | ) 29 | 30 | if exist %VISUAL_STUDIO_PATH_2% ( 31 | set VISUAL_STUDIO_PATH=%VISUAL_STUDIO_PATH_2% 32 | goto build 33 | ) 34 | 35 | if exist %VISUAL_STUDIO_PATH_3% ( 36 | set VISUAL_STUDIO_PATH=%VISUAL_STUDIO_PATH_3% 37 | goto build 38 | ) 39 | 40 | REM No default path found. Let the user know what to do. 41 | echo No Visual Studio installation found. Please configure it manually. 42 | echo 1. Copy 'Paths.cmd.template'. 43 | echo 2. Rename it to 'Paths.cmd'. 44 | echo 3. Enter your Visual Studio path in there. 45 | echo 4. Restart the build. 46 | REM Allow disabling pause to support non-interacting build chains. 47 | if NOT "%~1"=="-no-pause" pause 48 | goto end 49 | ) 50 | 51 | :build 52 | REM Log the used Vistual Studio version. 53 | @echo on 54 | %VISUAL_STUDIO_PATH% /p:Configuration=Release 55 | @echo off 56 | 57 | :end 58 | popd 59 | @echo on 60 | -------------------------------------------------------------------------------- /BuildInstaller.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION 3 | 4 | ECHO: 5 | ECHO === Starting Installer Build Workaround === 6 | 7 | REM Store current directory 8 | SET "current_path=%CD%" 9 | 10 | REM Try all known editions of Visual Studio 2022 11 | SET "vs_base_path=%ProgramFiles%\Microsoft Visual Studio\2022" 12 | FOR %%E IN (Community Professional Enterprise) DO ( 13 | IF EXIST "%vs_base_path%\%%E\Common7\IDE\CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild\DisableOutOfProcBuild.exe" ( 14 | SET "buildfix_path=%vs_base_path%\%%E\Common7\IDE\CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild" 15 | SET "devenv_path=%vs_base_path%\%%E\Common7\IDE\devenv.exe" 16 | SET "vs_edition=%%E" 17 | GOTO :FoundEdition 18 | ) 19 | ) 20 | 21 | ECHO [ERROR] Could not find DisableOutOfProcBuild.exe in any known VS2022 edition. 22 | EXIT /B 1 23 | 24 | :FoundEdition 25 | ECHO Found Visual Studio 2022 Edition: %vs_edition% 26 | CD /D "%buildfix_path%" 27 | CALL DisableOutOfProcBuild.exe 28 | 29 | REM Restore previous directory 30 | CD /D "%current_path%" 31 | 32 | REM === Validate required files === 33 | ECHO: 34 | ECHO === Checking required files === 35 | 36 | SET "error=0" 37 | 38 | IF NOT EXIST "UnityLauncherPro\bin\Release\UnityLauncherPro.exe" ( 39 | ECHO [ERROR] Missing file: UnityLauncherPro\bin\Release\UnityLauncherPro.exe 40 | SET "error=1" 41 | ) 42 | IF NOT EXIST "UnityLauncherPro\Images\icon.ico" ( 43 | ECHO [ERROR] Missing file: UnityLauncherPro\Images\icon.ico 44 | SET "error=1" 45 | ) 46 | 47 | IF %error% NEQ 0 ( 48 | ECHO [ERROR] Required files are missing. Aborting installer build. 49 | EXIT /B 1 50 | ) 51 | 52 | 53 | ECHO: 54 | ECHO === Building Installer === 55 | "%devenv_path%" UnityLauncherPro.sln /Rebuild Release /Project UnityLauncherProInstaller > build_output.log 2>&1 56 | SET "exitCode=%ERRORLEVEL%" 57 | 58 | TYPE build_output.log 59 | ECHO: 60 | ECHO === devenv.exe exit code: %exitCode% === 61 | 62 | IF NOT "%exitCode%"=="0" ( 63 | ECHO [ERROR] Installer build failed. Check build_output.log for details. 64 | EXIT /B %exitCode% 65 | ) 66 | 67 | 68 | ECHO: 69 | ECHO === Build Complete === 70 | 71 | REM Optional cleanup: disable workaround 72 | REG DELETE "HKCU\Software\Microsoft\VisualStudio\Setup" /v VSDisableOutOfProcBuild /f >NUL 2>&1 73 | 74 | ENDLOCAL 75 | EXIT /B %exitCode% 76 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to UnityLauncherPro 2 | 3 | Thanks for your interest in contributing! 4 | Here’s how you can help: 5 | 6 | ## How to Contribute 7 | 8 | 0. **Open Issue** Describe your idea first (if its useful, it might get accepted) OR check existing issues and comment there 9 | 1. **Fork** the repository 10 | 2. **Clone** your fork: `git clone https://github.com/YOURUSERNAME/UnityLauncherPro.git` 11 | 3. Optional: **Create a new branch**: `git checkout -b feature-name` 12 | 4. **Make your changes** 13 | 5. **Commit**: `git commit -m "Add feature XYZ"` 14 | 6. **Push**: `git push origin feature-name` 15 | 7. **Open a Pull Request** from your fork 16 | 17 | ## Reporting Bugs 18 | 19 | - Use the **Issues** tab 20 | - Include **steps to reproduce**, screenshots, and error messages 21 | 22 | ## Suggesting Features 23 | 24 | - Open an issue with `[Feature]` in the title 25 | - Describe the problem your feature solves 26 | - Note: Not all features get merged! *i'd like to keep workflows and UI something close to my own preferences (Sorry!), but you can of course make your own forks with additional features! 27 | 28 | ## Code Style & External Libraries 29 | 30 | - Style is not enforced 31 | - Simple code is good and functions can be as long as they need, to do the task (but can extract commonly used parts/methods into Tools or other helper libraries) 32 | - Avoid using external packages/nugets/dependencies (to keep this as a small single exe build) 33 | - Try to keep main features fast (or even improve existing features to make this more lightweight and faster to start etc.) 34 | 35 | ## Need Help? 36 | 37 | Feel free to open an issue or post in discussion. 38 | 39 | --- 40 | 41 | Thanks for contributing! 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 mika 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 | -------------------------------------------------------------------------------- /Paths.cmd.template: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Set your Visual Studio path here. 3 | SET VISUAL_STUDIO_PATH="%programfiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" 4 | @echo on -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityLauncherPro 2 | [![Build Status](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/main.yml/badge.svg)](https://github.com/unitycoder/UnityLauncherPro/releases/latest/download/UnityLauncherPro.zip) [![Downloads](https://img.shields.io/github/downloads/unitycoder/unitylauncherpro/total)](https://github.com/unitycoder/UnityLauncherPro/releases/latest/download/UnityLauncherPro.zip) [![GitHub license](https://img.shields.io/github/license/unitycoder/UnityLauncherPro)](https://github.com/unitycoder/UnityLauncherPro/blob/master/LICENSE) [](https://discord.gg/cXT97hU) [![VirusTotal scan now](https://img.shields.io/static/v1?label=VirusTotal&message=Scan)](https://www.virustotal.com/gui/url/e123b616cf4cbe3d3f7ba13b0d88cf5fff4638f72d5b9461088d0b11e9a41de3?nocache=1) [![CodeQL](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/codeql.yml/badge.svg)](https://github.com/unitycoder/UnityLauncherPro/actions/workflows/codeql.yml) 3 | 4 | Handle all your Unity versions and Projects easily! 5 | 6 | ## Features 7 | - Automagically Open Projects with Correct Unity Version 8 | - Display Recent Projects list with last modified date and project version info 9 | - List more than 40 recent projects! 10 | - Quickly Explore Project Folders 11 | - List installed Unity versions, can easily Run, Explore installation folder, View release notes 12 | - Download Missing Unity Versions in Browser 13 | - Can be used from commandline `UnityLauncherPro.exe -projectPath "c:/project/path/"` 14 | - Explorer context menu integration to launch project from folder 15 | - Use custom commandline launcher arguments per project (optional) 16 | - Show project git branch info (optional) 17 | - Current platform display and selection 18 | - Show list of released Unity versions/updates and view release notes page 19 | - 1-Click create new project with selected Unity version 20 | - Option to show missing projects in the list (if folder was removed) 21 | - 1-Click Start ADB Logcat with colors in command prompt 22 | - 1-Click browse editor log folder, crash logs folder, browse asset downloads folder, browse player logs folder 23 | - Quick Unity Editor Process Kill (press ALT+Q in the selected project row or right click context menu) 24 | - 1-Click start web server and launch WebGL build in browser for selected project 25 | - Custom skin color themes https://github.com/unitycoder/UnityLauncherPro/wiki/Custom-themes 26 | - Select template for new project (template based on selected unity version) 27 | - And more: https://github.com/unitycoder/UnityLauncherPro/wiki/Launcher-Comparison 28 | 29 | ### Powered by 30 | [![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) 31 | 32 | ### Forum Thread 33 | https://forum.unity.com/threads/unity-launcher-launch-correct-unity-versions-for-each-project-automatically.488718/ 34 | 35 | ### Instructions 36 | https://github.com/unitycoder/UnityLauncherPro/wiki 37 | 38 | ### Development 39 | See DEV branch for latest commits https://github.com/unitycoder/UnityLauncherPro/tree/dev
40 | Pre-releases are sometimes available from dev branch: https://github.com/unitycoder/UnityLauncherPro/releases 41 | 42 | ### Contributing 43 | See https://github.com/unitycoder/UnityLauncherPro/blob/master/CONTRIBUTING.md 44 | 45 | ### Screenshots 46 | 47 | ![Image](https://github.com/user-attachments/assets/80bd8ff4-7e90-4c1a-9501-74cf3ea538f6) 48 | 49 | ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/21eb1fcd-3cb1-4dea-8133-9ce440de77d8) 50 | 51 | ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/2942c1d7-2006-4dee-8b68-e1f6f2e0fbf4) 52 | 53 | ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/6f8dce07-c640-42db-a1ef-d8bcc7a80cc2) 54 | 55 | ![Image](https://github.com/user-attachments/assets/fa4e004a-f3c6-47d5-996f-9b603048ad18) 56 | 57 | ### Perform tasks on selected project 58 | ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/a0b468ba-e3a6-420b-8155-78bc32814752) 59 | 60 | ### Quick New Project Creation (with Unity version and Templates selection) 61 | ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/9b34f9ba-2c91-462c-be85-9c8aeefa63a0) 62 | 63 | ### Upgrade Project Version (automatically suggests next higher version) 64 | ![image](https://github.com/unitycoder/UnityLauncherPro/assets/5438317/3cc8f8d0-861b-406a-be10-716d92b7f379) 65 | 66 | ### Explorer integration (1-click opening projects with correct unity version) 67 | ![image](https://user-images.githubusercontent.com/5438317/120883135-34900080-c5e4-11eb-80a5-ae78fe89260a.png) 68 | 69 | ### View and Select current platform 70 | ![unitylauncherpro_platform](https://user-images.githubusercontent.com/5438317/132997691-5ed50f1d-e285-4c83-b356-46c1e3f431e4.gif) 71 | 72 | ### Rename Project title 73 | ![pro-rename](https://user-images.githubusercontent.com/5438317/193453048-aa5527c6-a345-4f93-b934-f68ac2524cac.gif) 74 | 75 |
76 | Old (winforms) version is here: https://github.com/unitycoder/UnityLauncher 77 | 78 | 79 | -------------------------------------------------------------------------------- /UnityLauncherPro.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.13.35931.197 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityLauncherPro", "UnityLauncherPro\UnityLauncherPro.csproj", "{EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}" 7 | EndProject 8 | Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "UnityLauncherProInstaller", "UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj", "{249180EF-BFC1-9DD7-48CC-E2B9253A64E0}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {249180EF-BFC1-9DD7-48CC-E2B9253A64E0}.Debug|Any CPU.ActiveCfg = Debug 21 | {249180EF-BFC1-9DD7-48CC-E2B9253A64E0}.Release|Any CPU.ActiveCfg = Release 22 | {249180EF-BFC1-9DD7-48CC-E2B9253A64E0}.Release|Any CPU.Build.0 = Release 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {CD70E364-F81A-402C-A387-1BEB396796A2} 29 | EndGlobalSection 30 | GlobalSection(Performance) = preSolution 31 | HasPerformanceSessions = true 32 | EndGlobalSection 33 | GlobalSection(Performance) = preSolution 34 | HasPerformanceSessions = true 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /UnityLauncherPro/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 600 18 | 19 | 20 | 650 21 | 22 | 23 | True 24 | 25 | 26 | False 27 | 28 | 29 | False 30 | 31 | 32 | True 33 | 34 | 35 | False 36 | 37 | 38 | False 39 | 40 | 41 | False 42 | 43 | 44 | False 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | True 54 | 55 | 56 | False 57 | 58 | 59 | False 60 | 61 | 62 | False 63 | 64 | 65 | 66 | 67 | 68 | False 69 | 70 | 71 | theme.ini 72 | 73 | 74 | False 75 | 76 | 77 | False 78 | 79 | 80 | False 81 | 82 | 83 | dd/MM/yyyy HH:mm:ss 84 | 85 | 86 | False 87 | 88 | 89 | False 90 | 91 | 92 | -s Unity ActivityManager PackageManager dalvikvm DEBUG -v color 93 | 94 | 95 | 0 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | False 105 | 106 | 107 | False 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | C:\Program Files\ 116 | 117 | 118 | 119 | 120 | InitializeProject.cs 121 | 122 | 123 | False 124 | 125 | 126 | 50000 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | False 135 | 136 | 137 | 40 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /UnityLauncherPro/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace UnityLauncherPro 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UnityLauncherPro/Converters/LastModifiedConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace UnityLauncherPro.Converters 6 | { 7 | // https://stackoverflow.com/a/14283973/5452781 8 | [ValueConversion(typeof(DateTime), typeof(String))] 9 | public class LastModifiedConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | // TODO: without this, editor mode fails with null references.. but would be nice to get rid of if's.. 14 | if (value == null) return null; 15 | 16 | DateTime date = (DateTime)value; 17 | 18 | return MainWindow.useHumanFriendlyDateFormat ? Tools.GetElapsedTime(date) : date.ToString(MainWindow.currentDateFormat); 19 | } 20 | 21 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 22 | { 23 | return DateTime.ParseExact((string)value, MainWindow.currentDateFormat, culture); 24 | } 25 | 26 | } 27 | 28 | // just for tooltip 29 | [ValueConversion(typeof(DateTime), typeof(String))] 30 | public class LastModifiedConverterTooltip : IValueConverter 31 | { 32 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 33 | { 34 | if (value == null) return null; 35 | DateTime date = (DateTime)value; 36 | return date.ToString(MainWindow.currentDateFormat); 37 | } 38 | 39 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 40 | { 41 | return DateTime.ParseExact((string)value, MainWindow.currentDateFormat, culture); 42 | } 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UnityLauncherPro/Converters/ReleaseDateConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace UnityLauncherPro.Converters 7 | { 8 | [ValueConversion(typeof(DateTime), typeof(string))] 9 | public class ReleaseDateConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value == null || !(value is DateTime date)) 14 | { 15 | return DependencyProperty.UnsetValue; 16 | } 17 | 18 | // Use a default date format if currentDateFormat is null or empty 19 | string dateStrTrimmed = MainWindow.currentDateFormat ?? "MM/dd/yyyy"; 20 | 21 | // If the format includes time, use only the date portion 22 | if (dateStrTrimmed.Contains(" ")) 23 | { 24 | dateStrTrimmed = dateStrTrimmed.Split(' ')[0]; 25 | } 26 | 27 | // Return a human-friendly format if enabled; otherwise, format based on dateStrTrimmed 28 | return MainWindow.useHumanFriendlyDateFormat 29 | ? Tools.GetElapsedTime(date) 30 | : date.ToString(dateStrTrimmed, culture); 31 | } 32 | 33 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 34 | { 35 | if (value == null || string.IsNullOrWhiteSpace(value.ToString())) 36 | { 37 | return DependencyProperty.UnsetValue; 38 | } 39 | 40 | // Attempt to parse back to DateTime using the specified format 41 | if (DateTime.TryParseExact((string)value, MainWindow.currentDateFormat ?? "MM/dd/yyyy", culture, DateTimeStyles.None, out DateTime parsedDate)) 42 | { 43 | return parsedDate; 44 | } 45 | 46 | return DependencyProperty.UnsetValue; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/BuildReport.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UnityLauncherPro 4 | { 5 | public class BuildReport 6 | { 7 | public long ElapsedTimeMS { set; get; } 8 | public List Stats { set; get; } // overal per category sizes 9 | public List Items { set; get; } // report rows 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/BuildReportItem.cs: -------------------------------------------------------------------------------- 1 | namespace UnityLauncherPro 2 | { 3 | public class BuildReportItem 4 | { 5 | // TODO use real values, so can sort and convert kb/mb 6 | public string Category { set; get; } // for category list 7 | public string Size { set; get; } 8 | public string Percentage { set; get; } 9 | public string Path { set; get; } 10 | public string Format { set; get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/DownloadProgress.cs: -------------------------------------------------------------------------------- 1 | namespace UnityLauncherPro 2 | { 3 | public readonly struct DownloadProgress 4 | { 5 | public long TotalRead { get; } 6 | public long TotalBytes { get; } 7 | 8 | public DownloadProgress(long totalRead, long totalBytes) 9 | { 10 | TotalRead = totalRead; 11 | TotalBytes = totalBytes; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /UnityLauncherPro/Data/MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace UnityLauncherPro.Data 2 | { 3 | public enum MessageType 4 | { 5 | Info, 6 | Warning, 7 | Error 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/Platform.cs: -------------------------------------------------------------------------------- 1 | namespace UnityLauncherPro 2 | { 3 | // TODO should display only valid platforms for projects Unity version 4 | public enum Platform 5 | { 6 | Unknown, 7 | Win32, Win64, OSX, Linux, Linux64, iOS, Android, Web, WebStreamed, WebGL, Xboxone, OS4, PSP2, WSAPlayer, Tizen, SamsungTV, 8 | Standalone, Win, OSXuniversal, LinuxUniversal, WindowsStoreApps, @Switch, Wiiu, N3DS, tVoS, PSM, 9 | // manually added 10 | StandaloneWindows, StandaloneWindows64 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/Project.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace UnityLauncherPro 6 | { 7 | public class Project : IValueConverter 8 | { 9 | public string Title { set; get; } 10 | public string Version { set; get; } 11 | public string Path { set; get; } 12 | public DateTime? Modified { set; get; } 13 | public string Arguments { set; get; } 14 | public string GITBranch { set; get; } // TODO rename to Branch 15 | //public string TargetPlatform { set; get; } 16 | public string TargetPlatform { set; get; } // TODO rename to Platform 17 | public string[] TargetPlatforms { set; get; } 18 | public bool folderExists { set; get; } 19 | public string SRP { set; get; } // Scriptable Render Pipeline, TODO add version info? 20 | 21 | // WPF keeps calling this method from AppendFormatHelper, GetNameCore..? not sure if need to return something else or default would be faster? 22 | public override string ToString() 23 | { 24 | return Path; 25 | } 26 | 27 | // for debugging 28 | //public override string ToString() 29 | //{ 30 | // return $"{Title} {Version} {Path} {Modified} {Arguments} {GITBranch} {TargetPlatform}"; 31 | //} 32 | 33 | // change datagrid colors based on value using converter https://stackoverflow.com/a/5551986/5452781 34 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 35 | { 36 | bool b = (bool)value; 37 | return b; 38 | } 39 | 40 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 41 | { 42 | throw new NotSupportedException(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /UnityLauncherPro/Data/Tabs.cs: -------------------------------------------------------------------------------- 1 | namespace UnityLauncherPro 2 | { 3 | public enum Tabs 4 | { 5 | Projects = 0, 6 | Unitys = 1, 7 | Updates = 2, 8 | Settings = 3 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/ThemeColor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using System.Windows.Media; 5 | 6 | namespace UnityLauncherPro 7 | { 8 | public class ThemeColor : IValueConverter 9 | { 10 | public string Key { get; set; } 11 | public SolidColorBrush Brush { get; set; } 12 | 13 | 14 | object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | return (SolidColorBrush)value; 17 | } 18 | 19 | object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/UnityInstallation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | 4 | namespace UnityLauncherPro 5 | { 6 | public class UnityInstallation : IValueConverter 7 | { 8 | public string Version { set; get; } 9 | public long VersionCode { set; get; } // version as number, cached for sorting 10 | public string Path { set; get; } // exe path 11 | public DateTime? Installed { set; get; } 12 | public string PlatformsCombined { set; get; } 13 | public string[] Platforms { set; get; } 14 | public int ProjectCount { set; get; } 15 | public bool IsPreferred { set; get; } 16 | public string ReleaseType { set; get; } // Alpha, Beta, LTS.. TODO could be enum 17 | 18 | // https://stackoverflow.com/a/5551986/5452781 19 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 20 | { 21 | string version = value as string; 22 | if (string.IsNullOrEmpty(version)) return null; 23 | return MainWindow.unityInstalledVersions.ContainsKey(version); 24 | } 25 | 26 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 27 | { 28 | throw new NotSupportedException(); 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/UnityVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace UnityLauncherPro 5 | { 6 | public class UnityVersion 7 | { 8 | public string Version { get; set; } 9 | public UnityVersionStream Stream { get; set; } 10 | public DateTime ReleaseDate { get; set; } 11 | public string directURL { get; set; } = null; // if found from unofficial releases list 12 | 13 | public static UnityVersion FromJson(string json) 14 | { 15 | var values = ParseJsonToDictionary(json); 16 | 17 | return new UnityVersion 18 | { 19 | Version = values.ContainsKey("version") ? values["version"] : null, 20 | Stream = ParseStream(values.ContainsKey("stream") ? values["stream"] : null), 21 | ReleaseDate = DateTime.TryParse(values.ContainsKey("releaseDate") ? values["releaseDate"] : null, out var date) 22 | ? date 23 | : default 24 | }; 25 | } 26 | 27 | public string ToJson() 28 | { 29 | return $"{{ \"version\": \"{Version}\", \"stream\": \"{Stream}\", \"releaseDate\": \"{ReleaseDate:yyyy-MM-ddTHH:mm:ss}\" }}"; 30 | } 31 | 32 | private static Dictionary ParseJsonToDictionary(string json) 33 | { 34 | var result = new Dictionary(); 35 | json = json.Trim(new char[] { '{', '}', ' ' }); 36 | var keyValuePairs = json.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); 37 | 38 | foreach (var pair in keyValuePairs) 39 | { 40 | var keyValue = pair.Split(new[] { ':' }, 2); 41 | if (keyValue.Length == 2) 42 | { 43 | var key = keyValue[0].Trim(new char[] { ' ', '"' }); 44 | var value = keyValue[1].Trim(new char[] { ' ', '"' }); 45 | result[key] = value; 46 | } 47 | } 48 | 49 | return result; 50 | } 51 | 52 | private static UnityVersionStream ParseStream(string stream) 53 | { 54 | return Enum.TryParse(stream, true, out UnityVersionStream result) ? result : UnityVersionStream.Tech; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /UnityLauncherPro/Data/UnityVersionResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UnityLauncherPro 4 | { 5 | public class UnityVersionResponse 6 | { 7 | public int Offset { get; set; } 8 | public int Limit { get; set; } 9 | public int Total { get; set; } 10 | public List Results { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /UnityLauncherPro/Data/UnityVersionStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace UnityLauncherPro 5 | { 6 | public enum UnityVersionStream 7 | { 8 | Alpha, 9 | Beta, 10 | LTS, 11 | Tech 12 | } 13 | 14 | public class UnityVersionJSON 15 | { 16 | public string Version { get; set; } 17 | public DateTime ReleaseDate { get; set; } 18 | public UnityVersionStream Stream { get; set; } 19 | public List Downloads { get; set; } 20 | public string ShortRevision { get; set; } 21 | } 22 | 23 | public class Download 24 | { 25 | public string Url { get; set; } 26 | public string Type { get; set; } 27 | public string Platform { get; set; } 28 | public string Architecture { get; set; } 29 | public DownloadSize DownloadSize { get; set; } 30 | public List Modules { get; set; } 31 | } 32 | 33 | public class DownloadSize 34 | { 35 | public long Value { get; set; } 36 | public string Unit { get; set; } 37 | } 38 | 39 | public class Module 40 | { 41 | public string Id { get; set; } 42 | public string Name { get; set; } 43 | public string Description { get; set; } 44 | public string Url { get; set; } 45 | } 46 | } -------------------------------------------------------------------------------- /UnityLauncherPro/DownloadProgressWindow.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /UnityLauncherPro/NewProject.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Input; 7 | using System.Windows.Media; 8 | 9 | namespace UnityLauncherPro 10 | { 11 | public partial class NewProject : Window 12 | { 13 | public static string newProjectName = null; 14 | public static string newVersion = null; 15 | public static string newName = null; 16 | public static string templateZipPath = null; 17 | public static string selectedPlatform = null; 18 | public static bool forceDX11 = false; 19 | public static string[] platformsForThisUnity = null; 20 | 21 | bool isInitializing = true; // to keep OnChangeEvent from firing too early 22 | int previousSelectedTemplateIndex = -1; 23 | int previousSelectedModuleIndex = -1; 24 | 25 | static string targetFolder = null; 26 | 27 | public NewProject(string unityVersion, string suggestedName, string targetFolder, bool nameIsLocked = false) 28 | { 29 | isInitializing = true; 30 | InitializeComponent(); 31 | 32 | NewProject.targetFolder = targetFolder; 33 | 34 | // get version 35 | newVersion = unityVersion; 36 | newName = suggestedName; 37 | 38 | txtNewProjectName.IsEnabled = !nameIsLocked; 39 | 40 | txtNewProjectName.Text = newName; 41 | lblNewProjectFolder.Content = targetFolder; 42 | 43 | // fill available versions 44 | if (gridAvailableVersions.ItemsSource == null) 45 | { 46 | gridAvailableVersions.ItemsSource = MainWindow.unityInstallationsSource; 47 | } 48 | 49 | // we have that version installed 50 | if (MainWindow.unityInstalledVersions.ContainsKey(unityVersion) == true) 51 | { 52 | // find this unity version, TODO theres probably easier way than looping all 53 | for (int i = 0; i < MainWindow.unityInstallationsSource.Count; i++) 54 | { 55 | if (MainWindow.unityInstallationsSource[i].Version == newVersion) 56 | { 57 | gridAvailableVersions.SelectedIndex = i; 58 | gridAvailableVersions.ScrollIntoView(gridAvailableVersions.SelectedItem); 59 | break; 60 | } 61 | } 62 | 63 | UpdateTemplatesDropDown((gridAvailableVersions.SelectedItem as UnityInstallation).Path); 64 | UpdateModulesDropdown(newVersion); 65 | } 66 | else // we dont have requested unity version, select first item then 67 | { 68 | var path = MainWindow.unityInstallationsSource[0].Path; 69 | gridAvailableVersions.SelectedIndex = 0; 70 | gridAvailableVersions.ScrollIntoView(gridAvailableVersions.Items[0]); 71 | UpdateTemplatesDropDown(path); 72 | } 73 | 74 | // select projectname text so can overwrite if needed 75 | txtNewProjectName.Focus(); 76 | txtNewProjectName.SelectAll(); 77 | newProjectName = txtNewProjectName.Text; 78 | 79 | isInitializing = false; 80 | } // NewProject 81 | 82 | void UpdateTemplatesDropDown(string unityPath) 83 | { 84 | // scan available templates, TODO could cache this at least per session? 85 | cmbNewProjectTemplate.ItemsSource = Tools.ScanTemplates(unityPath); 86 | cmbNewProjectTemplate.SelectedIndex = 0; 87 | lblTemplateTitleAndCount.Content = "Templates: (" + (cmbNewProjectTemplate.Items.Count - 1) + ")"; 88 | } 89 | 90 | 91 | void UpdateModulesDropdown(string version) 92 | { 93 | // get modules and stick into combobox, NOTE we already have this info from GetProjects.Scan, so could access it 94 | platformsForThisUnity = Tools.GetPlatformsForUnityVersion(version); 95 | cmbNewProjectPlatform.ItemsSource = platformsForThisUnity; 96 | 97 | var lastUsedPlatform = Properties.Settings.Default.newProjectPlatform; 98 | 99 | for (int i = 0; i < platformsForThisUnity.Length; i++) 100 | { 101 | // set default platform (win64) if never used this setting before 102 | if ((string.IsNullOrEmpty(lastUsedPlatform) && platformsForThisUnity[i].ToLower() == "win64") || platformsForThisUnity[i] == lastUsedPlatform) 103 | { 104 | cmbNewProjectPlatform.SelectedIndex = i; 105 | break; 106 | } 107 | } 108 | 109 | // if nothing found, use win64 110 | if (cmbNewProjectPlatform.SelectedIndex == -1) 111 | { 112 | //cmbNewProjectPlatform.SelectedIndex = cmbNewProjectPlatform.Items.Count > 1 ? 1 : 0; 113 | for (int i = 0; i < platformsForThisUnity.Length; i++) 114 | { 115 | if (platformsForThisUnity[i].ToLower() == "win64") 116 | { 117 | cmbNewProjectPlatform.SelectedIndex = i; 118 | break; 119 | } 120 | } 121 | 122 | // if still nothing, use first 123 | if (cmbNewProjectPlatform.SelectedIndex == -1) cmbNewProjectPlatform.SelectedIndex = 0; 124 | //lblTemplateTitleAndCount.Content = "Templates: (" + (cmbNewProjectTemplate.Items.Count - 1) + ")"; 125 | } 126 | } 127 | 128 | private void BtnCreateNewProject_Click(object sender, RoutedEventArgs e) 129 | { 130 | // check if projectname already exists (only if should be automatically created name) 131 | var targetPath = Path.Combine(targetFolder, txtNewProjectName.Text); 132 | if (txtNewProjectName.IsEnabled == true && Directory.Exists(targetPath) == true) 133 | { 134 | Tools.SetStatus("Project already exists: " + txtNewProjectName.Text); 135 | return; 136 | } 137 | 138 | templateZipPath = ((KeyValuePair)cmbNewProjectTemplate.SelectedValue).Value; 139 | selectedPlatform = cmbNewProjectPlatform.SelectedValue.ToString(); 140 | UpdateSelectedVersion(); 141 | 142 | // save last used value for platform 143 | Properties.Settings.Default.newProjectPlatform = cmbNewProjectPlatform.SelectedValue.ToString(); 144 | Properties.Settings.Default.Save(); 145 | 146 | DialogResult = true; 147 | } 148 | 149 | private void BtnCancelNewProject_Click(object sender, RoutedEventArgs e) 150 | { 151 | DialogResult = false; 152 | } 153 | 154 | 155 | private void Window_PreviewKeyDown(object sender, KeyEventArgs e) 156 | { 157 | switch (e.Key) 158 | { 159 | case Key.Tab: 160 | // manually tab into next component (automatic tabstops not really working here) 161 | TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next); 162 | UIElement keyboardFocus = Keyboard.FocusedElement as UIElement; 163 | if (keyboardFocus != null) 164 | { 165 | keyboardFocus.MoveFocus(tRequest); 166 | } 167 | break; 168 | case Key.F2: // select project name field 169 | txtNewProjectName.Focus(); 170 | txtNewProjectName.SelectAll(); 171 | break; 172 | case Key.F3: // next platform 173 | cmbNewProjectPlatform.SelectedIndex = ++cmbNewProjectPlatform.SelectedIndex % cmbNewProjectPlatform.Items.Count; 174 | break; 175 | case Key.F4: // next template 176 | case Key.Oem5: // select next template §-key 177 | cmbNewProjectTemplate.SelectedIndex = ++cmbNewProjectTemplate.SelectedIndex % cmbNewProjectTemplate.Items.Count; 178 | e.Handled = true; // override writing to textbox 179 | break; 180 | case Key.Enter: // enter, create proj 181 | BtnCreateNewProject_Click(null, null); 182 | e.Handled = true; 183 | break; 184 | case Key.Escape: // esc cancel 185 | // if pressed esc while combobox is open, close that one instead of closing window 186 | if (cmbNewProjectTemplate.IsDropDownOpen) 187 | { 188 | cmbNewProjectTemplate.IsDropDownOpen = false; 189 | if (previousSelectedTemplateIndex > -1) cmbNewProjectTemplate.SelectedIndex = previousSelectedTemplateIndex; 190 | return; 191 | } 192 | 193 | if (cmbNewProjectPlatform.IsDropDownOpen) 194 | { 195 | cmbNewProjectPlatform.IsDropDownOpen = false; 196 | if (previousSelectedModuleIndex > -1) cmbNewProjectPlatform.SelectedIndex = previousSelectedModuleIndex; 197 | return; 198 | } 199 | 200 | DialogResult = false; 201 | e.Handled = true; 202 | break; 203 | default: 204 | break; 205 | } 206 | } 207 | 208 | void UpdateSelectedVersion() 209 | { 210 | var k = gridAvailableVersions.SelectedItem as UnityInstallation; 211 | if (k != null && k.Version != newVersion) 212 | { 213 | newVersion = k.Version; 214 | } 215 | } 216 | 217 | private void TxtNewProjectName_TextChanged(object sender, TextChangedEventArgs e) 218 | { 219 | if (isInitializing == true) return; 220 | 221 | // warning yellow if contains space at start or end 222 | if (txtNewProjectName.Text.StartsWith(" ") || txtNewProjectName.Text.EndsWith(" ")) 223 | { 224 | // NOTE txtbox outline didnt work 225 | txtNewProjectName.Background = Brushes.Yellow; 226 | txtNewProjectStatus.Text = "Warning: Project name starts or ends with SPACE character"; 227 | txtNewProjectStatus.Foreground = Brushes.Orange; 228 | } 229 | else 230 | { 231 | // NOTE this element is not using themes yet, so can set white 232 | txtNewProjectName.Background = Brushes.White; 233 | txtNewProjectStatus.Foreground = Brushes.White; 234 | txtNewProjectStatus.Text = ""; 235 | } 236 | 237 | // validate new projectname that it doesnt exists already 238 | var targetPath = Path.Combine(targetFolder, txtNewProjectName.Text); 239 | if (Directory.Exists(targetPath) == true) 240 | { 241 | System.Console.WriteLine("Project already exists"); 242 | txtNewProjectName.BorderBrush = Brushes.Red; // not visible if focused 243 | txtNewProjectName.ToolTip = "Project folder already exists"; 244 | btnCreateNewProject.IsEnabled = false; 245 | } 246 | else 247 | { 248 | txtNewProjectName.BorderBrush = null; 249 | btnCreateNewProject.IsEnabled = true; 250 | txtNewProjectName.ToolTip = ""; 251 | } 252 | 253 | //System.Console.WriteLine("newProjectName: " + txtNewProjectName.Text); 254 | 255 | newProjectName = txtNewProjectName.Text; 256 | } 257 | 258 | private void TxtNewProjectName_PreviewKeyDown(object sender, KeyEventArgs e) 259 | { 260 | switch (e.Key) 261 | { 262 | case Key.PageUp: 263 | case Key.PageDown: 264 | case Key.Up: 265 | case Key.Down: 266 | Tools.SetFocusToGrid(gridAvailableVersions); 267 | break; 268 | default: 269 | break; 270 | } 271 | } 272 | 273 | void GenerateNewName() 274 | { 275 | var newProj = Tools.GetSuggestedProjectName(newVersion, lblNewProjectFolder.Content.ToString()); 276 | txtNewProjectName.Text = newProj; 277 | } 278 | 279 | // FIXME this gets called when list is updated? 280 | private void GridAvailableVersions_SelectionChanged(object sender, SelectionChangedEventArgs e) 281 | { 282 | if (gridAvailableVersions.SelectedItem == null || isInitializing == true) return; 283 | // new row selected, generate new project name for this version 284 | var k = gridAvailableVersions.SelectedItem as UnityInstallation; 285 | newVersion = k.Version; 286 | // no new name, if field is locked (because its folder name then) 287 | if (txtNewProjectName.IsEnabled == true) GenerateNewName(); 288 | 289 | // update templates list for selected unity version 290 | UpdateTemplatesDropDown(k.Path); 291 | UpdateModulesDropdown(k.Version); 292 | 293 | // hide forceDX11 checkbox if version is below 6000 294 | bool is6000 = k.Version.Contains("6000"); 295 | chkForceDX11.Visibility = is6000 ? Visibility.Visible : Visibility.Collapsed; 296 | } 297 | 298 | private void GridAvailableVersions_Loaded(object sender, RoutedEventArgs e) 299 | { 300 | // set initial default row color 301 | DataGridRow row = (DataGridRow)gridAvailableVersions.ItemContainerGenerator.ContainerFromIndex(gridAvailableVersions.SelectedIndex); 302 | // if no unitys available 303 | if (row == null) return; 304 | //row.Background = Brushes.Green; 305 | row.Foreground = Brushes.White; 306 | row.FontWeight = FontWeights.Bold; 307 | } 308 | 309 | private void CmbNewProjectTemplate_DropDownOpened(object sender, System.EventArgs e) 310 | { 311 | // on open, take current selection, so can undo later 312 | previousSelectedTemplateIndex = cmbNewProjectTemplate.SelectedIndex; 313 | } 314 | 315 | private void CmbNewProjectPlatform_DropDownOpened(object sender, System.EventArgs e) 316 | { 317 | previousSelectedModuleIndex = cmbNewProjectPlatform.SelectedIndex; 318 | } 319 | 320 | private void gridAvailableVersions_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) 321 | { 322 | // check that we clicked actually on a row 323 | var src = VisualTreeHelper.GetParent((DependencyObject)e.OriginalSource); 324 | var srcType = src.GetType(); 325 | if (srcType == typeof(ContentPresenter)) 326 | { 327 | BtnCreateNewProject_Click(null, null); 328 | } 329 | } 330 | 331 | private void chkForceDX11_Checked(object sender, RoutedEventArgs e) 332 | { 333 | forceDX11 = chkForceDX11.IsChecked == true; 334 | } 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /UnityLauncherPro/ProjectProperties.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /UnityLauncherPro/ProjectProperties.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Input; 8 | 9 | namespace UnityLauncherPro 10 | { 11 | /// 12 | /// Interaction logic for ProjectProperties.xaml 13 | /// 14 | public partial class ProjectProperties : Window 15 | { 16 | Project proj; 17 | 18 | public ProjectProperties(Project proj) 19 | { 20 | this.proj = proj; 21 | InitializeComponent(); 22 | } 23 | 24 | private void Window_PreviewKeyDown(object sender, KeyEventArgs e) 25 | { 26 | 27 | } 28 | 29 | private void btnCloseProperties_Click(object sender, RoutedEventArgs e) 30 | { 31 | DialogResult = false; 32 | } 33 | 34 | private void txtCustomEnvVariables_PreviewKeyDown(object sender, KeyEventArgs e) 35 | { 36 | 37 | } 38 | 39 | private void txtCustomEnvVariables_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) 40 | { 41 | // TODO validate 42 | } 43 | 44 | private void btnApplyProperties_Click(object sender, RoutedEventArgs e) 45 | { 46 | DialogResult = true; 47 | 48 | // TODO save settings to usersettings folder 49 | Tools.SaveProjectSettings(proj, txtCustomEnvVariables.Text); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /UnityLauncherPro/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("UnityLauncherPro")] 11 | [assembly: AssemblyDescription("Unity Hub Alternative")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("UnityCoder.com")] 14 | [assembly: AssemblyProduct("UnityLauncherPro")] 15 | [assembly: AssemblyCopyright("Copyright © 2025")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.1")] 55 | [assembly: AssemblyFileVersion("1.0.0.1")] 56 | -------------------------------------------------------------------------------- /UnityLauncherPro/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UnityLauncherPro.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityLauncherPro.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /UnityLauncherPro/Properties/Resources.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 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /UnityLauncherPro/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 880 7 | 8 | 9 | 650 10 | 11 | 12 | True 13 | 14 | 15 | False 16 | 17 | 18 | False 19 | 20 | 21 | True 22 | 23 | 24 | False 25 | 26 | 27 | False 28 | 29 | 30 | 31 | 32 | 33 | False 34 | 35 | 36 | True 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | True 46 | 47 | 48 | True 49 | 50 | 51 | False 52 | 53 | 54 | True 55 | 56 | 57 | 58 | 59 | 60 | False 61 | 62 | 63 | theme.ini 64 | 65 | 66 | True 67 | 68 | 69 | False 70 | 71 | 72 | False 73 | 74 | 75 | dd/MM/yyyy HH:mm:ss 76 | 77 | 78 | True 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | False 88 | 89 | 90 | -s Unity ActivityManager PackageManager dalvikvm DEBUG -v color 91 | 92 | 93 | 0 94 | 95 | 96 | 97 | 98 | 99 | win64 100 | 101 | 102 | False 103 | 104 | 105 | False 106 | 107 | 108 | 109 | 110 | 111 | <?xml version="1.0" encoding="utf-16"?> 112 | <ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 113 | <string>C:\Program Files\Unity\Hub\Editor\</string> 114 | <string>C:\Program Files\</string> 115 | </ArrayOfString> 116 | 117 | 118 | False 119 | 120 | 121 | 50000 122 | 123 | 124 | <?xml version="1.0" encoding="utf-16"?> 125 | <ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /> 126 | 127 | 128 | True 129 | 130 | 131 | 50 132 | 133 | 134 | https://raw.githubusercontent.com/unitycoder/UnityInitializeProject/main/Assets/Editor/InitializeProject.cs 135 | 136 | 137 | 138 | 139 | 140 | True 141 | 142 | 143 | False 144 | 145 | 146 | True 147 | 148 | 149 | False 150 | 151 | 152 | False 153 | 154 | 155 | False 156 | 157 | 158 | False 159 | 160 | 161 | -------------------------------------------------------------------------------- /UnityLauncherPro/Resources/Colors.xaml: -------------------------------------------------------------------------------- 1 |  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 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /UnityLauncherPro/ThemeEditor.xaml: -------------------------------------------------------------------------------- 1 |  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 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /UnityLauncherPro/ThemeEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.ComponentModel; 7 | using System.IO; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | 13 | namespace UnityLauncherPro 14 | { 15 | public partial class ThemeEditor : Window 16 | { 17 | static ObservableCollection themeColors = new ObservableCollection(); 18 | static ObservableCollection themeColorsOrig = new ObservableCollection(); 19 | 20 | string previousSaveFileName = null; 21 | 22 | // hack for adjusting slider, without triggering onchange.. 23 | bool forceValue = false; 24 | 25 | // for single undo 26 | Slider previousSlider; 27 | int previousValue = -1; 28 | 29 | public ThemeEditor() 30 | { 31 | InitializeComponent(); 32 | } 33 | 34 | private void Window_Loaded(object sender, RoutedEventArgs e) 35 | { 36 | themeColors.Clear(); 37 | themeColorsOrig.Clear(); 38 | 39 | // get original colors to collection 40 | foreach (DictionaryEntry item in Application.Current.Resources.MergedDictionaries[0]) 41 | { 42 | // take currently used colors 43 | var currentColor = (SolidColorBrush)Application.Current.Resources[item.Key]; 44 | 45 | var themeColorPair = new ThemeColor(); 46 | themeColorPair.Key = item.Key.ToString(); 47 | themeColorPair.Brush = currentColor; 48 | themeColors.Add(themeColorPair); 49 | 50 | // take backup copy 51 | var themeColorPair2 = new ThemeColor(); 52 | themeColorPair2.Key = item.Key.ToString(); 53 | themeColorPair2.Brush = currentColor; 54 | themeColorsOrig.Add(themeColorPair2); 55 | } 56 | // display current theme keys and values 57 | gridThemeColors.ItemsSource = themeColors; 58 | 59 | // sort by key sa default 60 | gridThemeColors.Items.SortDescriptions.Add(new SortDescription("Key", ListSortDirection.Ascending)); 61 | 62 | gridThemeColors.SelectedIndex = 0; 63 | } 64 | 65 | void UpdateColorPreview() 66 | { 67 | var newColor = new Color(); 68 | newColor.R = (byte)sliderRed.Value; 69 | newColor.G = (byte)sliderGreen.Value; 70 | newColor.B = (byte)sliderBlue.Value; 71 | newColor.A = (byte)sliderAlpha.Value; 72 | var newColorBrush = new SolidColorBrush(newColor); 73 | rectSelectedColor.Fill = newColorBrush; 74 | 75 | // set new color into our collection values 76 | themeColors[themeColors.IndexOf((ThemeColor)gridThemeColors.SelectedItem)].Brush = newColorBrush; 77 | 78 | gridThemeColors.Items.Refresh(); 79 | 80 | // apply color changes to mainwindow 81 | var item = gridThemeColors.SelectedItem as ThemeColor; 82 | Application.Current.Resources[item.Key] = newColorBrush; 83 | forceValue = false; 84 | } 85 | 86 | void SetSlider(Slider target, double color) 87 | { 88 | forceValue = true; 89 | target.Value = color; 90 | forceValue = false; 91 | } 92 | 93 | private void GridThemeColors_SelectionChanged(object sender, SelectionChangedEventArgs e) 94 | { 95 | if (gridThemeColors.SelectedIndex == -1) return; 96 | 97 | var item = gridThemeColors.SelectedItem as ThemeColor; 98 | if (item == null) return; 99 | 100 | // update preview box 101 | rectSelectedColor.Fill = item.Brush; 102 | 103 | // update RGBA sliders 104 | SetSlider(sliderRed, item.Brush.Color.R); 105 | SetSlider(sliderGreen, item.Brush.Color.G); 106 | SetSlider(sliderBlue, item.Brush.Color.B); 107 | SetSlider(sliderAlpha, item.Brush.Color.A); 108 | } 109 | 110 | private void BtnSaveTheme_Click(object sender, RoutedEventArgs e) 111 | { 112 | // 1) Determine the default filename (with .ini) 113 | string defaultName = string.IsNullOrEmpty(previousSaveFileName) 114 | ? "custom.ini" 115 | : previousSaveFileName + ".ini"; 116 | 117 | // 2) Ask the helper for a safe full path 118 | string initialFullPath = Tools.GetSafeFilePath("Themes", defaultName); 119 | string initialDir = Path.GetDirectoryName(initialFullPath); 120 | string initialFile = Path.GetFileNameWithoutExtension(initialFullPath); 121 | 122 | // 3) Configure the save dialog 123 | var saveFileDialog = new SaveFileDialog 124 | { 125 | FileName = initialFile, // no extension here 126 | DefaultExt = ".ini", 127 | Filter = "Theme files (.ini)|*.ini", 128 | InitialDirectory = initialDir, 129 | RestoreDirectory = true 130 | }; 131 | 132 | // 4) Show and, if confirmed, write out the INI 133 | if (saveFileDialog.ShowDialog() == true) 134 | { 135 | // Build INI lines 136 | var iniRows = new List 137 | { 138 | "# Created with UnityLauncherPro built-in theme editor " 139 | + DateTime.Now.ToString("dd/MM/yyyy") 140 | }; 141 | // original-style loop 142 | for (int i = 0; i < themeColors.Count; i++) 143 | { 144 | iniRows.Add(themeColors[i].Key + "=" + themeColors[i].Brush.ToString()); 145 | } 146 | 147 | // Get the chosen path & ensure its folder exists 148 | string themePath = saveFileDialog.FileName; 149 | previousSaveFileName = Path.GetFileNameWithoutExtension(themePath); 150 | Directory.CreateDirectory(Path.GetDirectoryName(themePath)); 151 | 152 | // Write out 153 | File.WriteAllLines(themePath, iniRows); 154 | Console.WriteLine("Saved theme: " + themePath); 155 | } 156 | } 157 | 158 | private void BtnResetTheme_Click(object sender, RoutedEventArgs e) 159 | { 160 | for (int i = 0; i < themeColorsOrig.Count; i++) 161 | { 162 | // reset collection colors 163 | themeColors[i].Brush = themeColorsOrig[i].Brush; 164 | 165 | // reset application colors 166 | Application.Current.Resources[themeColors[i].Key] = themeColorsOrig[i].Brush; 167 | } 168 | 169 | // reset current color 170 | if (gridThemeColors.SelectedItem != null) 171 | { 172 | var item = gridThemeColors.SelectedItem as ThemeColor; 173 | SetSlider(sliderRed, item.Brush.Color.R); 174 | SetSlider(sliderGreen, item.Brush.Color.G); 175 | SetSlider(sliderBlue, item.Brush.Color.B); 176 | SetSlider(sliderAlpha, item.Brush.Color.A); 177 | } 178 | 179 | UpdateColorPreview(); 180 | gridThemeColors.Items.Refresh(); 181 | } 182 | 183 | private void SliderRed_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 184 | { 185 | // onchanged is called before other components are ready..thanks wpf :D 186 | if (forceValue == true || txtRed == null) return; 187 | UpdateColorPreview(); 188 | } 189 | 190 | private void SliderGreen_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 191 | { 192 | if (forceValue == true || txtGreen == null) return; 193 | UpdateColorPreview(); 194 | } 195 | 196 | private void SliderBlue_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 197 | { 198 | if (forceValue == true || txtBlue == null) return; 199 | UpdateColorPreview(); 200 | } 201 | 202 | public void Executed_Undo(object sender, ExecutedRoutedEventArgs e) 203 | { 204 | // restore previous color 205 | SetSlider(previousSlider, previousValue); 206 | UpdateColorPreview(); 207 | } 208 | 209 | public void CanExecute_Undo(object sender, CanExecuteRoutedEventArgs e) 210 | { 211 | e.CanExecute = previousValue > -1; 212 | } 213 | 214 | private void SliderAlpha_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) 215 | { 216 | if (forceValue == true) return; 217 | if (txtAlpha == null) return; 218 | UpdateColorPreview(); 219 | } 220 | 221 | public void Executed_Save(object sender, ExecutedRoutedEventArgs e) 222 | { 223 | BtnSaveTheme_Click(null, null); 224 | } 225 | 226 | public void CanExecute_Save(object sender, CanExecuteRoutedEventArgs e) 227 | { 228 | e.CanExecute = true; 229 | } 230 | 231 | private void SliderRed_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 232 | { 233 | SetUndoValues(sender, txtRed); 234 | } 235 | 236 | private void SliderGreen_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 237 | { 238 | SetUndoValues(sender, txtGreen); 239 | } 240 | 241 | private void SliderBlue_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 242 | { 243 | SetUndoValues(sender, txtBlue); 244 | } 245 | 246 | private void SliderAlpha_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 247 | { 248 | SetUndoValues(sender, txtAlpha); 249 | } 250 | 251 | void SetUndoValues(Object sender, TextBox textBox) 252 | { 253 | previousSlider = (Slider)sender; 254 | previousValue = (int)previousSlider.Value; 255 | } 256 | 257 | private void TxtRed_KeyUp(object sender, KeyEventArgs e) 258 | { 259 | GetColorFromTextBox((TextBox)sender, sliderRed); 260 | } 261 | 262 | private void TxtGreen_KeyUp(object sender, KeyEventArgs e) 263 | { 264 | GetColorFromTextBox((TextBox)sender, sliderGreen); 265 | } 266 | 267 | private void TxtBlue_KeyUp(object sender, KeyEventArgs e) 268 | { 269 | GetColorFromTextBox((TextBox)sender, sliderBlue); 270 | } 271 | 272 | private void TxtAlpha_KeyUp(object sender, KeyEventArgs e) 273 | { 274 | GetColorFromTextBox((TextBox)sender, sliderAlpha); 275 | } 276 | 277 | void GetColorFromTextBox(TextBox source, Slider target) 278 | { 279 | int col = 0; 280 | if (int.TryParse(source.Text, out col)) 281 | { 282 | bool overWrite = false; 283 | if (col < 0) { col = 0; overWrite = true; } 284 | if (col > 255) { col = 255; overWrite = true; } 285 | 286 | source.Text = col + ""; 287 | target.Value = col; 288 | 289 | if (overWrite == true) source.SelectAll(); 290 | } 291 | } 292 | 293 | private void TxtColorField_PreviewKeyDown(object sender, KeyEventArgs e) 294 | { 295 | switch (e.Key) 296 | { 297 | case Key.Escape: // undo current textbox edit 298 | ((TextBox)sender).Undo(); 299 | break; 300 | } 301 | } 302 | } // class 303 | } // namespace -------------------------------------------------------------------------------- /UnityLauncherPro/UnityLauncherPro.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EC78D91A-3E63-4CAA-8BC3-9673A30FDA45} 8 | WinExe 9 | UnityLauncherPro 10 | UnityLauncherPro 11 | v4.8 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | true 17 | publish\ 18 | true 19 | Disk 20 | false 21 | Foreground 22 | 7 23 | Days 24 | false 25 | false 26 | true 27 | 0 28 | 1.0.0.%2a 29 | false 30 | false 31 | true 32 | 33 | 34 | 35 | AnyCPU 36 | true 37 | full 38 | false 39 | bin\Debug\ 40 | DEBUG;TRACE 41 | prompt 42 | 4 43 | 44 | 45 | AnyCPU 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | 53 | 54 | Images/icon.ico 55 | 56 | 57 | 58 | app.manifest 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 4.0 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | MSBuild:Compile 84 | Designer 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | DownloadProgressWindow.xaml 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | NewProject.xaml 109 | 110 | 111 | ProjectProperties.xaml 112 | 113 | 114 | ThemeEditor.xaml 115 | 116 | 117 | 118 | 119 | UpgradeWindow.xaml 120 | 121 | 122 | 123 | 124 | Designer 125 | MSBuild:Compile 126 | 127 | 128 | Designer 129 | MSBuild:Compile 130 | 131 | 132 | Designer 133 | MSBuild:Compile 134 | 135 | 136 | MSBuild:Compile 137 | Designer 138 | 139 | 140 | App.xaml 141 | Code 142 | 143 | 144 | MainWindow.xaml 145 | Code 146 | 147 | 148 | Designer 149 | MSBuild:Compile 150 | 151 | 152 | Designer 153 | MSBuild:Compile 154 | 155 | 156 | 157 | 158 | 159 | Code 160 | 161 | 162 | True 163 | True 164 | Resources.resx 165 | 166 | 167 | True 168 | Settings.settings 169 | True 170 | 171 | 172 | ResXFileCodeGenerator 173 | Resources.Designer.cs 174 | 175 | 176 | 177 | SettingsSingleFileGenerator 178 | Settings.Designer.cs 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | False 187 | Microsoft .NET Framework 4.6.1 %28x86 and x64%29 188 | true 189 | 190 | 191 | False 192 | .NET Framework 3.5 SP1 193 | false 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /UnityLauncherPro/UpgradeWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 17 | 60 | 61 | -------------------------------------------------------------------------------- /UnityLauncherPro/UpgradeWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Input; 5 | using System.Windows.Media; 6 | 7 | namespace UnityLauncherPro 8 | { 9 | /// 10 | /// Interaction logic for UpgradeWindow.xaml 11 | /// 12 | public partial class UpgradeWindow : Window 13 | { 14 | public static string upgradeVersion = null; 15 | 16 | public UpgradeWindow(string currentVersion, string projectPath, string commandLineArguments = null) 17 | { 18 | InitializeComponent(); 19 | txtCurrentVersion.Text = currentVersion; 20 | txtCurrentPlatform.Text = Tools.GetTargetPlatform(projectPath); 21 | 22 | if (gridAvailableVersions.ItemsSource == null) 23 | { 24 | gridAvailableVersions.ItemsSource = MainWindow.unityInstallationsSource; 25 | } 26 | 27 | gridAvailableVersions.SelectedItem = null; 28 | 29 | // we have current version info in project 30 | // enable release and dl buttons 31 | btnOpenReleasePage.IsEnabled = true; 32 | btnDownload.IsEnabled = true; 33 | 34 | // if dont have exact version, show red outline 35 | if (currentVersion == null || MainWindow.unityInstalledVersions.ContainsKey(currentVersion) == false) 36 | { 37 | txtCurrentVersion.BorderBrush = Brushes.Red; 38 | txtCurrentVersion.BorderThickness = new Thickness(1); 39 | } 40 | 41 | if (currentVersion != null) 42 | { 43 | // remove china c1 from version 44 | if (currentVersion.Contains("c")) currentVersion = currentVersion.Replace("c1", ""); 45 | // find nearest version 46 | string nearestVersion = Tools.FindNearestVersion(currentVersion, MainWindow.unityInstalledVersions.Keys.ToList()); 47 | 48 | if (nearestVersion != null) 49 | { 50 | // select nearest version 51 | for (int i = 0; i < MainWindow.unityInstallationsSource.Count; i++) 52 | { 53 | if (MainWindow.unityInstallationsSource[i].Version == nearestVersion) 54 | { 55 | gridAvailableVersions.SelectedIndex = i; 56 | gridAvailableVersions.ScrollIntoView(gridAvailableVersions.SelectedItem); 57 | break; 58 | } 59 | } 60 | } 61 | } 62 | 63 | gridAvailableVersions.Focus(); 64 | } 65 | 66 | 67 | private void BtnUpgradeProject_Click(object sender, RoutedEventArgs e) 68 | { 69 | Upgrade(); 70 | } 71 | 72 | private void BtnCancelUpgrade_Click(object sender, RoutedEventArgs e) 73 | { 74 | DialogResult = false; 75 | } 76 | 77 | private void BtnOpenReleasePage_Click(object sender, RoutedEventArgs e) 78 | { 79 | Tools.OpenReleaseNotes(txtCurrentVersion.Text); 80 | } 81 | 82 | 83 | private void BtnDownloadEditor_Click(object sender, RoutedEventArgs e) 84 | { 85 | Tools.DownloadInBrowser(txtCurrentVersion.Text); 86 | } 87 | 88 | private void BtnDownload_Click(object sender, RoutedEventArgs e) 89 | { 90 | Tools.DownloadInBrowser(txtCurrentVersion.Text); 91 | } 92 | 93 | private void btnInstall_Click(object sender, RoutedEventArgs e) 94 | { 95 | Tools.DownloadAndInstall(txtCurrentVersion.Text); 96 | } 97 | 98 | private void Window_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) 99 | { 100 | // override Enter for datagrid 101 | if (e.Key == Key.Return && e.KeyboardDevice.Modifiers == ModifierKeys.None) 102 | { 103 | e.Handled = true; 104 | var k = (UnityInstallation)gridAvailableVersions.SelectedItem; 105 | upgradeVersion = k.Version; 106 | DialogResult = true; 107 | return; 108 | } 109 | else // other keys 110 | { 111 | switch (e.Key) 112 | { 113 | case Key.Escape: 114 | DialogResult = false; 115 | break; 116 | default: 117 | break; 118 | } 119 | } 120 | 121 | base.OnKeyDown(e); 122 | } 123 | 124 | private void GridAvailableVersions_PreviewKeyDown(object sender, KeyEventArgs e) 125 | { 126 | Tools.HandleDataGridScrollKeys(sender, e); 127 | } 128 | 129 | private void GridAvailableVersions_Loaded(object sender, RoutedEventArgs e) 130 | { 131 | Tools.SetFocusToGrid(gridAvailableVersions); 132 | 133 | // bolded for current item 134 | DataGridRow row = (DataGridRow)((DataGrid)sender).ItemContainerGenerator.ContainerFromIndex(gridAvailableVersions.SelectedIndex); 135 | if (row == null) return; 136 | row.Foreground = Brushes.White; 137 | row.FontWeight = FontWeights.Bold; 138 | } 139 | 140 | private void GridAvailableVersions_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) 141 | { 142 | var src = VisualTreeHelper.GetParent((DependencyObject)e.OriginalSource); 143 | var srcType = src.GetType(); 144 | if (srcType == typeof(ContentPresenter)) 145 | { 146 | Upgrade(); 147 | } 148 | } 149 | 150 | void Upgrade() 151 | { 152 | var k = (UnityInstallation)gridAvailableVersions.SelectedItem; 153 | upgradeVersion = k.Version; 154 | DialogResult = true; 155 | } 156 | 157 | 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /UnityLauncherPro/Version.cs: -------------------------------------------------------------------------------- 1 | namespace UnityLauncherPro 2 | { 3 | internal class Version 4 | { 5 | public static string Stamp = ""; 6 | } 7 | } -------------------------------------------------------------------------------- /UnityLauncherPro/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 60 | PerMonitorV2 61 | true 62 | 63 | 64 | 65 | 66 | 67 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | skip_tags: true 3 | image: Visual Studio 2022 4 | configuration: Release 5 | only_commits: 6 | message: /#build/ 7 | build: 8 | verbosity: minimal 9 | after_build: 10 | - cmd: 7z a UnityLauncherPro.zip %APPVEYOR_BUILD_FOLDER%\UnityLauncherPro\bin\Release\*.exe %APPVEYOR_BUILD_FOLDER%\UnityLauncherPro\bin\Release\*.exe.config 11 | artifacts: 12 | - path: UnityLauncherPro.zip 13 | name: deploy 14 | deploy: 15 | - provider: GitHub 16 | auth_token: 17 | secure: kmYSrl7Mx/PFDGcyC5gS/vpW2UJCVguEXZsQ0LtkfwmSzx+3noZOyPrbZQ8uWX2B 18 | artifact: deploy 19 | --------------------------------------------------------------------------------