├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── a-bug-report.md │ ├── b-enhancement-request.md │ ├── c-program-not-launching.md │ ├── config.yml │ ├── d-dlcs-not-unlocking.md │ └── e-false-positives.md ├── .gitignore ├── CreamInstaller.sln ├── CreamInstaller ├── Components │ ├── ContextMenuItem.cs │ ├── CustomForm.cs │ ├── CustomForm.resx │ ├── CustomTreeView.cs │ └── PlatformIdComparer.cs ├── CreamInstaller.csproj ├── Forms │ ├── DebugForm.Designer.cs │ ├── DebugForm.cs │ ├── DebugForm.resx │ ├── DialogForm.Designer.cs │ ├── DialogForm.cs │ ├── DialogForm.resx │ ├── InstallForm.Designer.cs │ ├── InstallForm.cs │ ├── InstallForm.resx │ ├── SelectDialogForm.Designer.cs │ ├── SelectDialogForm.cs │ ├── SelectDialogForm.resx │ ├── SelectForm.Designer.cs │ ├── SelectForm.cs │ ├── SelectForm.resx │ ├── UpdateForm.Designer.cs │ ├── UpdateForm.cs │ └── UpdateForm.resx ├── Platforms │ ├── Epic │ │ ├── EpicLibrary.cs │ │ ├── EpicStore.cs │ │ ├── GraphQL │ │ │ ├── Request.cs │ │ │ └── Response.cs │ │ ├── Heroic │ │ │ ├── HeroicAppData.cs │ │ │ └── HeroicLibrary.cs │ │ └── Manifest.cs │ ├── Paradox │ │ └── ParadoxLauncher.cs │ ├── Steam │ │ ├── AppDetails.cs │ │ ├── CmdAppDetails.cs │ │ ├── SteamCMD.WebAPI.cs │ │ ├── SteamCMD.cs │ │ ├── SteamLibrary.cs │ │ ├── SteamStore.cs │ │ ├── StoreAppDetails.cs │ │ └── ValveDataFile.cs │ └── Ubisoft │ │ └── UbisoftLibrary.cs ├── Program.cs ├── ProgramRelease.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ └── launchSettings.json ├── Resources │ ├── CreamAPI.cs │ ├── CreamAPI │ │ ├── steam_api.dll │ │ └── steam_api64.dll │ ├── Koaloader.cs │ ├── Koaloader │ │ ├── audioses-32 │ │ │ └── audioses.dll │ │ ├── audioses-64 │ │ │ └── audioses.dll │ │ ├── d3d10-32 │ │ │ └── d3d10.dll │ │ ├── d3d10-64 │ │ │ └── d3d10.dll │ │ ├── d3d11-32 │ │ │ └── d3d11.dll │ │ ├── d3d11-64 │ │ │ └── d3d11.dll │ │ ├── d3d9-32 │ │ │ └── d3d9.dll │ │ ├── d3d9-64 │ │ │ └── d3d9.dll │ │ ├── dinput8-32 │ │ │ └── dinput8.dll │ │ ├── dinput8-64 │ │ │ └── dinput8.dll │ │ ├── dwmapi-32 │ │ │ └── dwmapi.dll │ │ ├── dwmapi-64 │ │ │ └── dwmapi.dll │ │ ├── dxgi-32 │ │ │ └── dxgi.dll │ │ ├── dxgi-64 │ │ │ └── dxgi.dll │ │ ├── glu32-32 │ │ │ └── glu32.dll │ │ ├── glu32-64 │ │ │ └── glu32.dll │ │ ├── hid-32 │ │ │ └── hid.dll │ │ ├── hid-64 │ │ │ └── hid.dll │ │ ├── iphlpapi-32 │ │ │ └── iphlpapi.dll │ │ ├── iphlpapi-64 │ │ │ └── iphlpapi.dll │ │ ├── msasn1-32 │ │ │ └── msasn1.dll │ │ ├── msasn1-64 │ │ │ └── msasn1.dll │ │ ├── msimg32-32 │ │ │ └── msimg32.dll │ │ ├── msimg32-64 │ │ │ └── msimg32.dll │ │ ├── mswsock-32 │ │ │ └── mswsock.dll │ │ ├── mswsock-64 │ │ │ └── mswsock.dll │ │ ├── opengl32-32 │ │ │ └── opengl32.dll │ │ ├── opengl32-64 │ │ │ └── opengl32.dll │ │ ├── profapi-32 │ │ │ └── profapi.dll │ │ ├── profapi-64 │ │ │ └── profapi.dll │ │ ├── propsys-32 │ │ │ └── propsys.dll │ │ ├── propsys-64 │ │ │ └── propsys.dll │ │ ├── textshaping-32 │ │ │ └── textshaping.dll │ │ ├── textshaping-64 │ │ │ └── textshaping.dll │ │ ├── version-32 │ │ │ └── version.dll │ │ ├── version-64 │ │ │ └── version.dll │ │ ├── winhttp-32 │ │ │ └── winhttp.dll │ │ ├── winhttp-64 │ │ │ └── winhttp.dll │ │ ├── winmm-32 │ │ │ └── winmm.dll │ │ ├── winmm-64 │ │ │ └── winmm.dll │ │ ├── wldp-32 │ │ │ └── wldp.dll │ │ ├── wldp-64 │ │ │ └── wldp.dll │ │ ├── xinput9_1_0-32 │ │ │ └── xinput9_1_0.dll │ │ └── xinput9_1_0-64 │ │ │ └── xinput9_1_0.dll │ ├── Resources.cs │ ├── ScreamAPI.cs │ ├── ScreamAPI │ │ ├── EOSSDK-Win32-Shipping.dll │ │ └── EOSSDK-Win64-Shipping.dll │ ├── SmokeAPI.cs │ ├── SmokeAPI │ │ ├── steam_api.dll │ │ └── steam_api64.dll │ ├── UplayR1.cs │ ├── UplayR1 │ │ ├── uplay_r1_loader.dll │ │ └── uplay_r1_loader64.dll │ ├── UplayR2.cs │ ├── UplayR2 │ │ ├── upc_r2_loader.dll │ │ └── upc_r2_loader64.dll │ └── program.ico ├── Selection.cs ├── SelectionDLC.cs └── Utility │ ├── Diagnostics.cs │ ├── ExceptionHandler.cs │ ├── HttpClientManager.cs │ ├── IconGrabber.cs │ ├── LogTextBox.cs │ ├── NativeImports.cs │ ├── ProgramData.cs │ └── SafeIO.cs ├── Hashing ├── .gitignore └── GetHashes.ps1 ├── LICENSE ├── README.md └── preview.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/a-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a program exception or general bug, not including those explained within the FAQ and/or template issues. 4 | title: '' 5 | labels: Bug 6 | assignees: pointfeev 7 | 8 | --- 9 | 10 | ###### Describe the bug and/or provide an image of the exception dialog box: 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/b-enhancement-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Request a new program feature or an enhancement to an existing feature. 4 | title: '' 5 | labels: Enhancement 6 | assignees: pointfeev 7 | 8 | --- 9 | 10 | ###### Describe the new feature and its implementation OR how the existing feature could be improved: 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/c-program-not-launching.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Program not launching 3 | about: I likely can not do anything about this; see this template's content. 4 | title: I promise not to post another issue about the program not launching. 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | First and foremost, note that the program currently only supports Windows 8+ 64-bit machines, as .NET 7+ no longer support Windows 7. If that does not apply to you, then make sure you've extracted the executable from the ZIP file before you've launched it, resolved your anti-virus, and have tried downloading the .NET Desktop Runtime mentioned under [installation instructions](https://github.com/pointfeev/CreamInstaller#installation) and restarting your computer. If none of the above work, then I simply cannot do anything about it, I do not control .NET. Either your system is not supported by the current version of .NET, or something is wrong/corrupted with your system. 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/d-dlcs-not-unlocking.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Game not working/DLCs not unlocking 3 | about: This is not the place to report game-specific issues; see this template's content. 4 | title: I promise not to post another issue about game-specific unlocking issues. 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Make sure you've read the note under [Usage](https://github.com/pointfeev/CreamInstaller#usage)! Assuming the program functioned as it was supposed to by properly installing DLC unlockers to your chosen games, this is not an issue I can do anything about and it's entirely up to you to seek the appropriate resources to fix it yourself (hint: https://cs.rin.ru/forum/viewforum.php?f=10). 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/e-false-positives.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mamson.A!ac, Phonzy.A!ml, Wacatac.H!ml, Malgent!MSR, Tiggre!rfn, and many many others 3 | about: These are false positives; see this template's content. 4 | title: I promise not to post another issue about false positives. 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | The "issue" of the program's outputted Koaloader DLLs being detected as false positives such as Mamson.A!ac, Phonzy.A!ml, Wacatac.H!ml, Malgent!MSR, Tiggre!rfn, and many many others, has already been posted and explained dozens of times now in many different manners... please do not post it again, you will just be ignored; instead, refer to the explanations within issue #40 and its linked issues: https://github.com/pointfeev/CreamInstaller/issues/40. 11 | -------------------------------------------------------------------------------- /.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 | *.lnk 8 | *.rsuser 9 | *.suo 10 | *.user 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | [Ll]og/ 28 | 29 | # Visual Studio 2015/2017 cache/options directory 30 | .vs/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # Visual Studio 2017 auto generated files 35 | Generated\ Files/ 36 | 37 | # MSTest test Results 38 | [Tt]est[Rr]esult*/ 39 | [Bb]uild[Ll]og.* 40 | 41 | # NUNIT 42 | *.VisualState.xml 43 | TestResult.xml 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # Benchmark Results 51 | BenchmarkDotNet.Artifacts/ 52 | 53 | # .NET Core 54 | project.lock.json 55 | project.fragment.lock.json 56 | artifacts/ 57 | 58 | # StyleCop 59 | StyleCopReport.xml 60 | 61 | # Files built by Visual Studio 62 | *_i.c 63 | *_p.c 64 | *_h.h 65 | *.ilk 66 | *.meta 67 | *.obj 68 | *.iobj 69 | *.pch 70 | *.pdb 71 | *.ipdb 72 | *.pgc 73 | *.pgd 74 | *.rsp 75 | *.sbr 76 | *.tlb 77 | *.tli 78 | *.tlh 79 | *.tmp 80 | *.tmp_proj 81 | *_wpftmp.csproj 82 | *.log 83 | *.vspscc 84 | *.vssscc 85 | .builds 86 | *.pidb 87 | *.svclog 88 | *.scc 89 | 90 | # Chutzpah Test files 91 | _Chutzpah* 92 | 93 | # Visual C++ cache files 94 | ipch/ 95 | *.aps 96 | *.ncb 97 | *.opendb 98 | *.opensdf 99 | *.sdf 100 | *.cachefile 101 | *.VC.db 102 | *.VC.VC.opendb 103 | 104 | # Visual Studio profiler 105 | *.psess 106 | *.vsp 107 | *.vspx 108 | *.sap 109 | 110 | # Visual Studio Trace Files 111 | *.e2e 112 | 113 | # TFS 2012 Local Workspace 114 | $tf/ 115 | 116 | # Guidance Automation Toolkit 117 | *.gpState 118 | 119 | # ReSharper is a .NET coding add-in 120 | _ReSharper*/ 121 | *.[Rr]e[Ss]harper 122 | *.DotSettings.user 123 | 124 | # JustCode is a .NET coding add-in 125 | .JustCode 126 | 127 | # TeamCity is a build add-in 128 | _TeamCity* 129 | 130 | # DotCover is a Code Coverage Tool 131 | *.dotCover 132 | 133 | # AxoCover is a Code Coverage Tool 134 | .axoCover/* 135 | !.axoCover/settings.json 136 | 137 | # Visual Studio code coverage results 138 | *.coverage 139 | *.coveragexml 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | nCrunchTemp_* 145 | 146 | # MightyMoose 147 | *.mm.* 148 | AutoTest.Net/ 149 | 150 | # Web workbench (sass) 151 | .sass-cache/ 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # Note: Comment the next line if you want to checkin your web deploy settings, 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/[Pp]ackages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/[Pp]ackages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/[Pp]ackages/repositories.config 190 | # NuGet v3's project.json files produces more ignorable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Windows Store app package directories and files 203 | AppPackages/ 204 | BundleArtifacts/ 205 | Package.StoreAssociation.xml 206 | _pkginfo.txt 207 | *.appx 208 | 209 | # Visual Studio cache files 210 | # files ending in .cache can be ignored 211 | *.[Cc]ache 212 | # but keep track of directories ending in .cache 213 | !*.[Cc]ache/ 214 | 215 | # Others 216 | ClientBin/ 217 | ~$* 218 | *~ 219 | *.dbmdl 220 | *.dbproj.schemaview 221 | *.jfm 222 | *.pfx 223 | *.publishsettings 224 | orleans.codegen.cs 225 | 226 | # Including strong name files can present a security risk 227 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 228 | #*.snk 229 | 230 | # Since there are multiple workflows, uncomment next line to ignore bower_components 231 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 232 | #bower_components/ 233 | 234 | # RIA/Silverlight projects 235 | Generated_Code/ 236 | 237 | # Backup & report files from converting an old project file 238 | # to a newer Visual Studio version. Backup files are not needed, 239 | # because we have git ;-) 240 | _UpgradeReport_Files/ 241 | Backup*/ 242 | UpgradeLog*.XML 243 | UpgradeLog*.htm 244 | ServiceFabricBackup/ 245 | *.rptproj.bak 246 | 247 | # SQL Server files 248 | *.mdf 249 | *.ldf 250 | *.ndf 251 | 252 | # Business Intelligence projects 253 | *.rdl.data 254 | *.bim.layout 255 | *.bim_*.settings 256 | *.rptproj.rsuser 257 | 258 | # Microsoft Fakes 259 | FakesAssemblies/ 260 | 261 | # GhostDoc plugin setting file 262 | *.GhostDoc.xml 263 | 264 | # Node.js Tools for Visual Studio 265 | .ntvs_analysis.dat 266 | node_modules/ 267 | 268 | # Visual Studio 6 build log 269 | *.plg 270 | 271 | # Visual Studio 6 workspace options file 272 | *.opt 273 | 274 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 275 | *.vbw 276 | 277 | # Visual Studio LightSwitch build output 278 | **/*.HTMLClient/GeneratedArtifacts 279 | **/*.DesktopClient/GeneratedArtifacts 280 | **/*.DesktopClient/ModelManifest.xml 281 | **/*.Server/GeneratedArtifacts 282 | **/*.Server/ModelManifest.xml 283 | _Pvt_Extensions 284 | 285 | # Paket dependency manager 286 | .paket/paket.exe 287 | paket-files/ 288 | 289 | # FAKE - F# Make 290 | .fake/ 291 | 292 | # JetBrains Rider 293 | .idea/ 294 | *.sln.iml 295 | 296 | # CodeRush personal settings 297 | .cr/personal 298 | 299 | # Python Tools for Visual Studio (PTVS) 300 | __pycache__/ 301 | *.pyc 302 | 303 | # Cake - Uncomment if you are using it 304 | # tools/** 305 | # !tools/packages.config 306 | 307 | # Tabs Studio 308 | *.tss 309 | 310 | # Telerik's JustMock configuration file 311 | *.jmconfig 312 | 313 | # BizTalk build output 314 | *.btp.cs 315 | *.btm.cs 316 | *.odx.cs 317 | *.xsd.cs 318 | 319 | # OpenCover UI analysis results 320 | OpenCover/ 321 | 322 | # Azure Stream Analytics local run output 323 | ASALocalRun/ 324 | 325 | # MSBuild Binary and Structured Log 326 | *.binlog 327 | 328 | # NVidia Nsight GPU debugger configuration file 329 | *.nvuser 330 | 331 | # MFractors (Xamarin productivity tool) working folder 332 | .mfractor/ 333 | 334 | # Local History for Visual Studio 335 | .localhistory/ 336 | *.exe 337 | *.zip 338 | -------------------------------------------------------------------------------- /CreamInstaller.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32516.85 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreamInstaller", "CreamInstaller\CreamInstaller.csproj", "{6C94C882-7168-435E-B9E3-B4B9222BBF68}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {6C94C882-7168-435E-B9E3-B4B9222BBF68}.Debug|x64.ActiveCfg = Debug|x64 15 | {6C94C882-7168-435E-B9E3-B4B9222BBF68}.Debug|x64.Build.0 = Debug|x64 16 | {6C94C882-7168-435E-B9E3-B4B9222BBF68}.Release|x64.ActiveCfg = Release|x64 17 | {6C94C882-7168-435E-B9E3-B4B9222BBF68}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {5373FD20-3B35-4077-8E2A-EC976AFF05BB} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /CreamInstaller/Components/ContextMenuItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Drawing; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | using CreamInstaller.Platforms.Paradox; 7 | using CreamInstaller.Utility; 8 | 9 | namespace CreamInstaller.Components; 10 | 11 | internal sealed class ContextMenuItem : ToolStripMenuItem 12 | { 13 | private static readonly ConcurrentDictionary Images = new(); 14 | 15 | private readonly EventHandler onClickEvent; 16 | 17 | internal ContextMenuItem(string text, EventHandler onClick = null) 18 | { 19 | Text = text; 20 | onClickEvent = onClick; 21 | } 22 | 23 | internal ContextMenuItem(string text, string imageIdentifier, EventHandler onClick = null) : this(text, onClick) 24 | => _ = TryImageIdentifier(this, imageIdentifier); 25 | 26 | internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, EventHandler onClick = null) 27 | : this(text, onClick) 28 | => _ = TryImageIdentifierInfo(this, imageIdentifierInfo); 29 | 30 | internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, 31 | string imageIdentifierFallback, EventHandler onClick = null) : 32 | this(text, onClick) 33 | { 34 | async void OnFail() => await TryImageIdentifier(this, imageIdentifierFallback); 35 | _ = TryImageIdentifierInfo(this, imageIdentifierInfo, OnFail); 36 | } 37 | 38 | internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, 39 | (string id, string iconUrl) imageIdentifierInfoFallback, 40 | EventHandler onClick = null) : this(text, onClick) 41 | { 42 | async void OnFail() => await TryImageIdentifierInfo(this, imageIdentifierInfoFallback); 43 | _ = TryImageIdentifierInfo(this, imageIdentifierInfo, OnFail); 44 | } 45 | 46 | private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier) 47 | => await Task.Run(async () => 48 | { 49 | if (Images.TryGetValue(imageIdentifier, out Image image) && image is not null) 50 | item.Image = image; 51 | else 52 | { 53 | switch (imageIdentifier) 54 | { 55 | case "Paradox Launcher": 56 | if (ParadoxLauncher.InstallPath.DirectoryExists()) 57 | foreach (string file in ParadoxLauncher.InstallPath.EnumerateDirectory("*.exe")) 58 | { 59 | image = file.GetFileIconImage(); 60 | break; 61 | } 62 | 63 | break; 64 | case "Notepad": 65 | image = IconGrabber.GetNotepadImage(); 66 | break; 67 | case "Command Prompt": 68 | image = IconGrabber.GetCommandPromptImage(); 69 | break; 70 | case "File Explorer": 71 | image = IconGrabber.GetFileExplorerImage(); 72 | break; 73 | case "SteamDB": 74 | image = await HttpClientManager.GetImageFromUrl( 75 | IconGrabber.GetDomainFaviconUrl("steamdb.info")); 76 | break; 77 | case "Steam Store": 78 | image = await HttpClientManager.GetImageFromUrl( 79 | IconGrabber.GetDomainFaviconUrl("store.steampowered.com")); 80 | break; 81 | case "Steam Community": 82 | image = await HttpClientManager.GetImageFromUrl( 83 | IconGrabber.GetDomainFaviconUrl("steamcommunity.com")); 84 | break; 85 | case "ScreamDB": 86 | image = await HttpClientManager.GetImageFromUrl( 87 | IconGrabber.GetDomainFaviconUrl("scream-db.web.app")); 88 | break; 89 | case "Epic Games": 90 | image = await HttpClientManager.GetImageFromUrl( 91 | IconGrabber.GetDomainFaviconUrl("epicgames.com")); 92 | break; 93 | case "Ubisoft Store": 94 | image = await HttpClientManager.GetImageFromUrl( 95 | IconGrabber.GetDomainFaviconUrl("store.ubi.com")); 96 | break; 97 | default: 98 | return; 99 | } 100 | 101 | if (image is not null) 102 | { 103 | Images[imageIdentifier] = image; 104 | item.Image = image; 105 | } 106 | } 107 | }); 108 | 109 | private static async Task TryImageIdentifierInfo(ContextMenuItem item, 110 | (string id, string iconUrl) imageIdentifierInfo, Action onFail = null) 111 | => await Task.Run(async () => 112 | { 113 | try 114 | { 115 | (string id, string iconUrl) = imageIdentifierInfo; 116 | string imageIdentifier = "Icon_" + id; 117 | if (Images.TryGetValue(imageIdentifier, out Image image) && image is not null) 118 | item.Image = image; 119 | else 120 | { 121 | image = await HttpClientManager.GetImageFromUrl(iconUrl); 122 | if (image is not null) 123 | { 124 | Images[imageIdentifier] = image; 125 | item.Image = image; 126 | } 127 | else 128 | onFail?.Invoke(); 129 | } 130 | } 131 | catch 132 | { 133 | // ignored 134 | } 135 | }); 136 | 137 | protected override void OnClick(EventArgs e) 138 | { 139 | base.OnClick(e); 140 | onClickEvent?.Invoke(this, e); 141 | } 142 | } -------------------------------------------------------------------------------- /CreamInstaller/Components/CustomForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Drawing2D; 4 | using System.Drawing.Imaging; 5 | using System.Windows.Forms; 6 | using CreamInstaller.Forms; 7 | using CreamInstaller.Utility; 8 | 9 | namespace CreamInstaller.Components; 10 | 11 | internal class CustomForm : Form 12 | { 13 | internal CustomForm() 14 | { 15 | Icon = Properties.Resources.Icon; 16 | KeyPreview = true; 17 | KeyPress += OnKeyPress; 18 | ResizeRedraw = true; 19 | HelpButton = true; 20 | HelpButtonClicked += OnHelpButtonClicked; 21 | } 22 | 23 | internal CustomForm(IWin32Window owner) : this() 24 | { 25 | if (owner is not Form form) 26 | return; 27 | Owner = form; 28 | InheritLocation(form); 29 | SizeChanged += (_, _) => InheritLocation(form); 30 | form.Activated += OnActivation; 31 | FormClosing += (_, _) => form.Activated -= OnActivation; 32 | TopLevel = true; 33 | } 34 | 35 | protected override CreateParams CreateParams // Double buffering for all controls 36 | { 37 | get 38 | { 39 | CreateParams handleParam = base.CreateParams; 40 | handleParam.ExStyle |= 0x02; // WS_EX_COMPOSITED 41 | return handleParam; 42 | } 43 | } 44 | 45 | private void OnHelpButtonClicked(object sender, EventArgs args) 46 | { 47 | using DialogForm helpDialog = new(this); 48 | helpDialog.HelpButton = false; 49 | const string acidicoala = "https://github.com/acidicoala"; 50 | string repository = $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}"; 51 | _ = helpDialog.Show(SystemIcons.Information, 52 | "自动遍历Steam、Epic和Ubisoft的游戏DLC\n" 53 | + "解析SteamCMD、Steam Store和Epic Games Store获取游戏DLC\n" 54 | + "利用获取的信息解锁DLC\n\n" 55 | + $"应用使用最新的[CreamAPI](https://cs.rin.ru/forum/viewtopic.php?f=29&t=70576) by [deadmau5](https://cs.rin.ru/forum/viewtopic.php?f=29&t=70576). 最新的 [Koaloader]({acidicoala}/Koaloader), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1\n" 56 | + $"Unlocker]({acidicoala}/UplayR1Unlocker) 还有 [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), 全靠 [acidicoala]({acidicoala}). 上述工具已经全部集成到应用,无需额外下载\n" 57 | + "" 58 | + "如何使用:\n" + " 1. 选择游戏\n" 59 | + " 应用会自动遍历.\n" 60 | + " 2. 等待自动下载SteamWork (Steam游戏).\n" 61 | + " 3. 等待应用找到DLC信息\n" 62 | + " 第一次运行可能需要较久的时间\n" 63 | + " 4. 选择你需要解锁的DLC\n" 64 | + " 我们没有对全部游戏进行测试\n" 65 | + " 5. 选择时候使用Koaloader,如果是就选择代理Dll\n" 66 | + " 如果默认 \'version.dll\' 无法使用, 就打开[here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172)寻找能用的\n" 67 | + " 6. 点击 \"安装\" \n" 68 | + " 7. 点击 \"OK\"关闭应用\n" 69 | + " 8.如果解锁工具使游戏启动失败,就重新启动应用程序卸载解锁工具\n" 70 | + " 转到第五步,选择需要恢复的游戏进行卸载\n\n" 71 | + "注意: 程序不会帮你下载真实的DLC文件,比如(地平线5的车辆DLC包等)\n" 72 | + "他只是个解锁工具,如果你想解锁游戏没安装的DLC 例如地平线5\n" 73 | + "你需要自己去寻找DLC的资源包\n" 74 | + "如果DLC更新你需要手动安装更新后的DLC\n\n" 75 | + $"任何错误提交到GITHUB Issues 页面[GitHub Issues]({repository}/issues) \n\n" 76 | + $"不过: 请阅读 [FAQ entry]({repository}#faq--common-issues) 还有 [template issue]({repository}/issues/new/choose) [GitHub\n" 77 | + $"Issues]({repository}/issues) 页面不是你倒垃圾的页面,也不是你彰显你低智商的舞台,而是针对各类bug等问题的页面,你的任何傻逼死妈问题,请不要在此页面进行发布,否则你的亲妈会被大运撞死\n" 78 | + "如果你的问题已经在FAQ Template issues 作了解答,亲自己杀掉亲妈且关闭问题线程 \n" 79 | + "我不会解答此问题.\n\n" 80 | + "SteamCMD 的缓存目录: [C:\\ProgramData\\CreamInstaller]().\n" 81 | + $"程序会自动从项目检查更新 [GitHub]({repository}) \n" 82 | + $"源代码可以在我的github找到[GitHub]({repository})."); 83 | } 84 | 85 | private void OnActivation(object sender, EventArgs args) => Activate(); 86 | 87 | internal void BringToFrontWithoutActivation() 88 | { 89 | bool topMost = TopMost; 90 | NativeImports.SetWindowPos(Handle, NativeImports.HWND_TOPMOST, 0, 0, 0, 0, 91 | NativeImports.SWP_NOACTIVATE | NativeImports.SWP_SHOWWINDOW | NativeImports.SWP_NOMOVE | 92 | NativeImports.SWP_NOSIZE); 93 | if (!topMost) 94 | NativeImports.SetWindowPos(Handle, NativeImports.HWND_NOTOPMOST, 0, 0, 0, 0, 95 | NativeImports.SWP_NOACTIVATE | NativeImports.SWP_SHOWWINDOW | NativeImports.SWP_NOMOVE | 96 | NativeImports.SWP_NOSIZE); 97 | } 98 | 99 | internal void InheritLocation(Form fromForm) 100 | { 101 | if (fromForm is null) 102 | return; 103 | int X = fromForm.Location.X + fromForm.Size.Width / 2 - Size.Width / 2; 104 | int Y = fromForm.Location.Y + fromForm.Size.Height / 2 - Size.Height / 2; 105 | Location = new(X, Y); 106 | } 107 | 108 | private void OnKeyPress(object s, KeyPressEventArgs e) 109 | { 110 | if (e.KeyChar != 'S') 111 | return; // Shift + S 112 | UpdateBounds(); 113 | Rectangle bounds = Bounds; 114 | using Bitmap bitmap = new(Size.Width - 14, Size.Height - 7); 115 | using Graphics graphics = Graphics.FromImage(bitmap); 116 | graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 117 | using EncoderParameters encoding = new(1); 118 | using EncoderParameter encoderParam = new(Encoder.Quality, 100L); 119 | encoding.Param[0] = encoderParam; 120 | graphics.CopyFromScreen(new(bounds.Left + 7, bounds.Top), Point.Empty, new(Size.Width - 14, Size.Height - 7)); 121 | Clipboard.SetImage(bitmap); 122 | e.Handled = true; 123 | } 124 | } -------------------------------------------------------------------------------- /CreamInstaller/Components/CustomForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /CreamInstaller/Components/PlatformIdComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Windows.Forms; 5 | 6 | namespace CreamInstaller.Components; 7 | 8 | internal static class PlatformIdComparer 9 | { 10 | private static StringComparer stringComparer; 11 | private static NodeComparer nodeComparer; 12 | private static NodeNameComparer nodeNameComparer; 13 | private static NodeTextComparer nodeTextComparer; 14 | 15 | internal static StringComparer String => stringComparer ??= new(); 16 | internal static NodeComparer Node => nodeComparer ??= new(); 17 | internal static NodeNameComparer NodeName => nodeNameComparer ??= new(); 18 | internal static NodeTextComparer NodeText => nodeTextComparer ??= new(); 19 | } 20 | 21 | internal sealed class StringComparer : IComparer 22 | { 23 | public int Compare(string a, string b) 24 | => !int.TryParse(a, out _) && !int.TryParse(b, out _) 25 | ? string.Compare(a, b, StringComparison.CurrentCulture) 26 | : !int.TryParse(a, out int A) 27 | ? 1 28 | : !int.TryParse(b, out int B) 29 | ? -1 30 | : A > B 31 | ? 1 32 | : A < B 33 | ? -1 34 | : 0; 35 | } 36 | 37 | internal sealed class NodeComparer : IComparer 38 | { 39 | public int Compare(TreeNode a, TreeNode b) 40 | => a is null 41 | ? 1 42 | : b is null 43 | ? -1 44 | : a.Tag is not Platform pA || b.Tag is not Platform pB 45 | ? 0 46 | : pA > pB 47 | ? 1 48 | : pA < pB 49 | ? -1 50 | : 0; 51 | } 52 | 53 | internal sealed class NodeNameComparer : IComparer 54 | { 55 | public int Compare(object a, object b) 56 | => a is not TreeNode A 57 | ? 1 58 | : b is not TreeNode B 59 | ? -1 60 | : PlatformIdComparer.Node.Compare(A, B) is var c && c != 0 61 | ? c 62 | : PlatformIdComparer.String.Compare(A.Name, B.Name); 63 | } 64 | 65 | internal sealed class NodeTextComparer : IComparer 66 | { 67 | public int Compare(object a, object b) 68 | => a is not TreeNode A 69 | ? 1 70 | : b is not TreeNode B 71 | ? -1 72 | : PlatformIdComparer.Node.Compare(A, B) is var c && c != 0 73 | ? c 74 | : PlatformIdComparer.String.Compare(A.Text, B.Text); 75 | } -------------------------------------------------------------------------------- /CreamInstaller/Forms/DebugForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Forms; 3 | 4 | namespace CreamInstaller.Forms; 5 | 6 | partial class DebugForm 7 | { 8 | /// 9 | /// Required designer variable. 10 | /// 11 | private IContainer components = null; 12 | 13 | /// 14 | /// Clean up any resources being used. 15 | /// 16 | /// true if managed resources should be disposed; otherwise, false. 17 | protected override void Dispose(bool disposing) 18 | { 19 | if (disposing && (components != null)) 20 | { 21 | components.Dispose(); 22 | } 23 | base.Dispose(disposing); 24 | } 25 | 26 | #region Windows Form Designer generated code 27 | 28 | /// 29 | /// Required method for Designer support - do not modify 30 | /// the contents of this method with the code editor. 31 | /// 32 | private void InitializeComponent() 33 | { 34 | debugTextBox = new RichTextBox(); 35 | SuspendLayout(); 36 | // 37 | // debugTextBox 38 | // 39 | debugTextBox.Dock = DockStyle.Fill; 40 | debugTextBox.Location = new System.Drawing.Point(10, 10); 41 | debugTextBox.Name = "debugTextBox"; 42 | debugTextBox.ReadOnly = true; 43 | debugTextBox.ScrollBars = RichTextBoxScrollBars.ForcedBoth; 44 | debugTextBox.Size = new System.Drawing.Size(540, 317); 45 | debugTextBox.TabIndex = 0; 46 | debugTextBox.TabStop = false; 47 | debugTextBox.Text = ""; 48 | // 49 | // DebugForm 50 | // 51 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 52 | AutoScaleMode = AutoScaleMode.Font; 53 | ClientSize = new System.Drawing.Size(560, 337); 54 | ControlBox = false; 55 | Controls.Add(debugTextBox); 56 | FormBorderStyle = FormBorderStyle.FixedSingle; 57 | MaximizeBox = false; 58 | MinimizeBox = false; 59 | Name = "DebugForm"; 60 | Padding = new Padding(10); 61 | ShowIcon = false; 62 | ShowInTaskbar = false; 63 | StartPosition = FormStartPosition.Manual; 64 | Text = "Debug"; 65 | ResumeLayout(false); 66 | } 67 | 68 | #endregion 69 | 70 | private RichTextBox debugTextBox; 71 | } -------------------------------------------------------------------------------- /CreamInstaller/Forms/DebugForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Windows.Forms; 4 | using CreamInstaller.Components; 5 | using CreamInstaller.Utility; 6 | 7 | namespace CreamInstaller.Forms; 8 | 9 | internal sealed partial class DebugForm : CustomForm 10 | { 11 | private static DebugForm current; 12 | 13 | private Form attachedForm; 14 | 15 | private DebugForm() 16 | { 17 | InitializeComponent(); 18 | debugTextBox.BackColor = LogTextBox.Background; 19 | } 20 | 21 | internal static DebugForm Current 22 | { 23 | get 24 | { 25 | if (current is not null && (current.Disposing || current.IsDisposed)) 26 | current = null; 27 | return current ??= new(); 28 | } 29 | } 30 | 31 | protected override void WndProc(ref Message message) // make form immovable by user 32 | { 33 | if (message.Msg == 0x0112) // WM_SYSCOMMAND 34 | { 35 | int command = message.WParam.ToInt32() & 0xFFF0; 36 | if (command == 0xF010) // SC_MOVE 37 | return; 38 | } 39 | 40 | base.WndProc(ref message); 41 | } 42 | 43 | internal void Attach(Form form) 44 | { 45 | if (attachedForm is not null) 46 | { 47 | attachedForm.Activated -= OnChange; 48 | attachedForm.LocationChanged -= OnChange; 49 | attachedForm.SizeChanged -= OnChange; 50 | attachedForm.VisibleChanged -= OnChange; 51 | } 52 | 53 | attachedForm = form; 54 | attachedForm.Activated += OnChange; 55 | attachedForm.LocationChanged += OnChange; 56 | attachedForm.SizeChanged += OnChange; 57 | attachedForm.VisibleChanged += OnChange; 58 | UpdateAttachment(); 59 | } 60 | 61 | private void OnChange(object sender, EventArgs args) => UpdateAttachment(); 62 | 63 | private void UpdateAttachment() 64 | { 65 | if (attachedForm is null || !attachedForm.Visible) 66 | return; 67 | //Size = new(Size.Width, attachedForm.Size.Height); 68 | Location = new(attachedForm.Right, attachedForm.Top); 69 | BringToFrontWithoutActivation(); 70 | } 71 | 72 | internal void Log(string text) => Log(text, LogTextBox.Error); 73 | 74 | internal void Log(string text, Color color) 75 | { 76 | if (!debugTextBox.Disposing && !debugTextBox.IsDisposed) 77 | Invoke(() => 78 | { 79 | if (debugTextBox.Text.Length > 0) 80 | debugTextBox.AppendText(Environment.NewLine, color, true); 81 | debugTextBox.AppendText(text, color, true); 82 | }); 83 | } 84 | } -------------------------------------------------------------------------------- /CreamInstaller/Forms/DebugForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | text/microsoft-resx 111 | 112 | 113 | 2.0 114 | 115 | 116 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 117 | PublicKeyToken=b77a5c561934e089 118 | 119 | 120 | 121 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 122 | PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | 127 | True 128 | 129 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/DialogForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Forms; 3 | 4 | namespace CreamInstaller.Forms 5 | { 6 | partial class DialogForm 7 | { 8 | private IContainer components = null; 9 | protected override void Dispose(bool disposing) 10 | { 11 | if (disposing && components is not null) 12 | components.Dispose(); 13 | base.Dispose(disposing); 14 | } 15 | 16 | #region Windows Form Designer generated code 17 | 18 | /// 19 | /// Required method for Designer support - do not modify 20 | /// the contents of this method with the code editor. 21 | /// 22 | private void InitializeComponent() 23 | { 24 | cancelButton = new Button(); 25 | acceptButton = new Button(); 26 | descriptionLabel = new LinkLabel(); 27 | icon = new PictureBox(); 28 | descriptionPanel = new FlowLayoutPanel(); 29 | descriptionLabelPanel = new FlowLayoutPanel(); 30 | buttonPanel = new FlowLayoutPanel(); 31 | ((ISupportInitialize)icon).BeginInit(); 32 | descriptionPanel.SuspendLayout(); 33 | descriptionLabelPanel.SuspendLayout(); 34 | buttonPanel.SuspendLayout(); 35 | SuspendLayout(); 36 | // 37 | // cancelButton 38 | // 39 | cancelButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; 40 | cancelButton.AutoSize = true; 41 | cancelButton.AutoSizeMode = AutoSizeMode.GrowAndShrink; 42 | cancelButton.DialogResult = DialogResult.Cancel; 43 | cancelButton.Location = new System.Drawing.Point(136, 10); 44 | cancelButton.Name = "cancelButton"; 45 | cancelButton.Padding = new Padding(12, 0, 12, 0); 46 | cancelButton.Size = new System.Drawing.Size(115, 24); 47 | cancelButton.TabIndex = 1; 48 | cancelButton.Text = "cancelButton"; 49 | cancelButton.UseVisualStyleBackColor = true; 50 | // 51 | // acceptButton 52 | // 53 | acceptButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 54 | acceptButton.AutoSize = true; 55 | acceptButton.AutoSizeMode = AutoSizeMode.GrowAndShrink; 56 | acceptButton.DialogResult = DialogResult.OK; 57 | acceptButton.Location = new System.Drawing.Point(257, 9); 58 | acceptButton.Name = "acceptButton"; 59 | acceptButton.Padding = new Padding(12, 0, 12, 0); 60 | acceptButton.Size = new System.Drawing.Size(112, 25); 61 | acceptButton.TabIndex = 0; 62 | acceptButton.Text = "acceptButton"; 63 | acceptButton.UseVisualStyleBackColor = true; 64 | // 65 | // descriptionLabel 66 | // 67 | descriptionLabel.AutoSize = true; 68 | descriptionLabel.LinkArea = new LinkArea(0, 0); 69 | descriptionLabel.Location = new System.Drawing.Point(9, 0); 70 | descriptionLabel.Margin = new Padding(9, 0, 3, 0); 71 | descriptionLabel.Name = "descriptionLabel"; 72 | descriptionLabel.Size = new System.Drawing.Size(94, 15); 73 | descriptionLabel.TabIndex = 2; 74 | descriptionLabel.Text = "descriptionLabel"; 75 | // 76 | // icon 77 | // 78 | icon.Location = new System.Drawing.Point(15, 15); 79 | icon.Name = "icon"; 80 | icon.Size = new System.Drawing.Size(48, 48); 81 | icon.SizeMode = PictureBoxSizeMode.AutoSize; 82 | icon.TabIndex = 4; 83 | icon.TabStop = false; 84 | // 85 | // descriptionPanel 86 | // 87 | descriptionPanel.AutoSize = true; 88 | descriptionPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; 89 | descriptionPanel.Controls.Add(icon); 90 | descriptionPanel.Controls.Add(descriptionLabelPanel); 91 | descriptionPanel.Dock = DockStyle.Fill; 92 | descriptionPanel.Location = new System.Drawing.Point(0, 0); 93 | descriptionPanel.Margin = new Padding(0); 94 | descriptionPanel.Name = "descriptionPanel"; 95 | descriptionPanel.Padding = new Padding(12, 12, 12, 6); 96 | descriptionPanel.Size = new System.Drawing.Size(384, 72); 97 | descriptionPanel.TabIndex = 5; 98 | // 99 | // descriptionLabelPanel 100 | // 101 | descriptionLabelPanel.AutoScroll = true; 102 | descriptionLabelPanel.AutoSize = true; 103 | descriptionLabelPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; 104 | descriptionLabelPanel.Controls.Add(descriptionLabel); 105 | descriptionLabelPanel.Dock = DockStyle.Fill; 106 | descriptionLabelPanel.Location = new System.Drawing.Point(66, 12); 107 | descriptionLabelPanel.Margin = new Padding(0); 108 | descriptionLabelPanel.Name = "descriptionLabelPanel"; 109 | descriptionLabelPanel.Size = new System.Drawing.Size(106, 54); 110 | descriptionLabelPanel.TabIndex = 6; 111 | // 112 | // buttonPanel 113 | // 114 | buttonPanel.AutoSize = true; 115 | buttonPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; 116 | buttonPanel.Controls.Add(acceptButton); 117 | buttonPanel.Controls.Add(cancelButton); 118 | buttonPanel.Dock = DockStyle.Bottom; 119 | buttonPanel.FlowDirection = FlowDirection.RightToLeft; 120 | buttonPanel.Location = new System.Drawing.Point(0, 72); 121 | buttonPanel.Name = "buttonPanel"; 122 | buttonPanel.Padding = new Padding(12, 6, 0, 12); 123 | buttonPanel.Size = new System.Drawing.Size(384, 49); 124 | buttonPanel.TabIndex = 6; 125 | // 126 | // DialogForm 127 | // 128 | AcceptButton = acceptButton; 129 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 130 | AutoScaleMode = AutoScaleMode.Font; 131 | AutoSize = true; 132 | AutoSizeMode = AutoSizeMode.GrowAndShrink; 133 | CancelButton = cancelButton; 134 | ClientSize = new System.Drawing.Size(384, 121); 135 | Controls.Add(descriptionPanel); 136 | Controls.Add(buttonPanel); 137 | FormBorderStyle = FormBorderStyle.FixedSingle; 138 | MaximizeBox = false; 139 | MaximumSize = new System.Drawing.Size(1600, 900); 140 | MinimizeBox = false; 141 | MinimumSize = new System.Drawing.Size(400, 160); 142 | Name = "DialogForm"; 143 | ShowInTaskbar = false; 144 | StartPosition = FormStartPosition.CenterParent; 145 | Text = "DialogForm"; 146 | ((ISupportInitialize)icon).EndInit(); 147 | descriptionPanel.ResumeLayout(false); 148 | descriptionPanel.PerformLayout(); 149 | descriptionLabelPanel.ResumeLayout(false); 150 | descriptionLabelPanel.PerformLayout(); 151 | buttonPanel.ResumeLayout(false); 152 | buttonPanel.PerformLayout(); 153 | ResumeLayout(false); 154 | PerformLayout(); 155 | } 156 | 157 | #endregion 158 | 159 | private Button cancelButton; 160 | private Button acceptButton; 161 | private PictureBox icon; 162 | private FlowLayoutPanel descriptionPanel; 163 | private FlowLayoutPanel buttonPanel; 164 | private LinkLabel descriptionLabel; 165 | private FlowLayoutPanel descriptionLabelPanel; 166 | } 167 | } -------------------------------------------------------------------------------- /CreamInstaller/Forms/DialogForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.Windows.Forms; 6 | using CreamInstaller.Components; 7 | 8 | namespace CreamInstaller.Forms; 9 | 10 | internal sealed partial class DialogForm : CustomForm 11 | { 12 | internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); 13 | 14 | internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK", 15 | string cancelButtonText = null, 16 | string customFormText = null, Icon customFormIcon = null) 17 | { 18 | descriptionIcon ??= Icon; 19 | icon.Image = descriptionIcon?.ToBitmap(); 20 | List links = []; 21 | for (int i = 0; i < descriptionText.Length; i++) 22 | if (descriptionText[i] == '[') 23 | { 24 | int textLeft = descriptionText.IndexOf('[', i); 25 | int textRight = descriptionText.IndexOf(']', textLeft == -1 ? i : textLeft); 26 | int linkLeft = descriptionText.IndexOf('(', textRight == -1 ? i : textRight); 27 | int linkRight = descriptionText.IndexOf(')', linkLeft == -1 ? i : linkLeft); 28 | if (textLeft == -1 || textRight != linkLeft - 1 || linkRight == -1) 29 | continue; 30 | string text = descriptionText[(textLeft + 1)..textRight]; 31 | string link = descriptionText[(linkLeft + 1)..linkRight]; 32 | if (string.IsNullOrWhiteSpace(link)) 33 | link = text; 34 | descriptionText = descriptionText.Remove(i, linkRight + 1 - i).Insert(i, text); 35 | links.Add(new(i, text.Length, link)); 36 | } 37 | 38 | descriptionLabel.Text = descriptionText; 39 | acceptButton.Text = acceptButtonText; 40 | if (cancelButtonText is null) 41 | { 42 | cancelButton.Enabled = false; 43 | cancelButton.Visible = false; 44 | } 45 | else 46 | cancelButton.Text = cancelButtonText; 47 | 48 | if (customFormText is not null) 49 | Text = customFormText; 50 | else 51 | { 52 | OnResize(null, null); 53 | Resize += OnResize; 54 | } 55 | 56 | if (customFormIcon is not null) 57 | Icon = customFormIcon; 58 | if (links.Count < 1) 59 | return ShowDialog(); 60 | foreach (LinkLabel.Link link in links) 61 | _ = descriptionLabel.Links.Add(link); 62 | descriptionLabel.LinkClicked += (s, e) => 63 | { 64 | if (e.Link != null) 65 | _ = Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true }); 66 | }; 67 | return ShowDialog(); 68 | } 69 | 70 | private void OnResize(object s, EventArgs e) 71 | => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 72 | ? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 73 | ? Program.Name 74 | : Program.ApplicationNameShort 75 | : Program.ApplicationName; 76 | } -------------------------------------------------------------------------------- /CreamInstaller/Forms/DialogForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | text/microsoft-resx 111 | 112 | 113 | 2.0 114 | 115 | 116 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 117 | PublicKeyToken=b77a5c561934e089 118 | 119 | 120 | 121 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 122 | PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/InstallForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Forms; 3 | 4 | namespace CreamInstaller.Forms 5 | { 6 | sealed partial class InstallForm 7 | { 8 | private IContainer components = null; 9 | protected override void Dispose(bool disposing) 10 | { 11 | if (disposing && components is not null) 12 | components.Dispose(); 13 | base.Dispose(disposing); 14 | } 15 | 16 | #region Windows Form Designer generated code 17 | 18 | /// 19 | /// Required method for Designer support - do not modify 20 | /// the contents of this method with the code editor. 21 | /// 22 | private void InitializeComponent() 23 | { 24 | userProgressBar = new ProgressBar(); 25 | userInfoLabel = new Label(); 26 | acceptButton = new Button(); 27 | retryButton = new Button(); 28 | cancelButton = new Button(); 29 | logTextBox = new RichTextBox(); 30 | reselectButton = new Button(); 31 | SuspendLayout(); 32 | // 33 | // userProgressBar 34 | // 35 | userProgressBar.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; 36 | userProgressBar.Location = new System.Drawing.Point(12, 27); 37 | userProgressBar.Name = "userProgressBar"; 38 | userProgressBar.Size = new System.Drawing.Size(760, 23); 39 | userProgressBar.TabIndex = 1; 40 | // 41 | // userInfoLabel 42 | // 43 | userInfoLabel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; 44 | userInfoLabel.AutoEllipsis = true; 45 | userInfoLabel.Location = new System.Drawing.Point(12, 9); 46 | userInfoLabel.Name = "userInfoLabel"; 47 | userInfoLabel.Size = new System.Drawing.Size(760, 15); 48 | userInfoLabel.TabIndex = 2; 49 | userInfoLabel.Text = "Loading . . . "; 50 | // 51 | // acceptButton 52 | // 53 | acceptButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 54 | acceptButton.Enabled = false; 55 | acceptButton.Location = new System.Drawing.Point(697, 526); 56 | acceptButton.Name = "acceptButton"; 57 | acceptButton.Size = new System.Drawing.Size(75, 23); 58 | acceptButton.TabIndex = 4; 59 | acceptButton.Text = "OK"; 60 | acceptButton.UseVisualStyleBackColor = true; 61 | acceptButton.Click += OnAccept; 62 | // 63 | // retryButton 64 | // 65 | retryButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 66 | retryButton.Enabled = false; 67 | retryButton.Location = new System.Drawing.Point(616, 526); 68 | retryButton.Name = "retryButton"; 69 | retryButton.Size = new System.Drawing.Size(75, 23); 70 | retryButton.TabIndex = 3; 71 | retryButton.Text = "重试"; 72 | retryButton.UseVisualStyleBackColor = true; 73 | retryButton.Click += OnRetry; 74 | // 75 | // cancelButton 76 | // 77 | cancelButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; 78 | cancelButton.Location = new System.Drawing.Point(12, 526); 79 | cancelButton.Name = "cancelButton"; 80 | cancelButton.Size = new System.Drawing.Size(75, 23); 81 | cancelButton.TabIndex = 1; 82 | cancelButton.Text = "取消"; 83 | cancelButton.UseVisualStyleBackColor = true; 84 | cancelButton.Click += OnCancel; 85 | // 86 | // logTextBox 87 | // 88 | logTextBox.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; 89 | logTextBox.HideSelection = false; 90 | logTextBox.Location = new System.Drawing.Point(12, 56); 91 | logTextBox.Name = "logTextBox"; 92 | logTextBox.ReadOnly = true; 93 | logTextBox.ScrollBars = RichTextBoxScrollBars.ForcedBoth; 94 | logTextBox.Size = new System.Drawing.Size(760, 464); 95 | logTextBox.TabIndex = 4; 96 | logTextBox.TabStop = false; 97 | logTextBox.Text = ""; 98 | // 99 | // reselectButton 100 | // 101 | reselectButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 102 | reselectButton.Location = new System.Drawing.Point(410, 526); 103 | reselectButton.Name = "reselectButton"; 104 | reselectButton.Size = new System.Drawing.Size(200, 23); 105 | reselectButton.TabIndex = 2; 106 | reselectButton.Text = "返回选择"; 107 | reselectButton.UseVisualStyleBackColor = true; 108 | reselectButton.Click += OnReselect; 109 | // 110 | // InstallForm 111 | // 112 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 113 | AutoScaleMode = AutoScaleMode.Font; 114 | AutoSize = true; 115 | AutoSizeMode = AutoSizeMode.GrowAndShrink; 116 | ClientSize = new System.Drawing.Size(784, 561); 117 | Controls.Add(reselectButton); 118 | Controls.Add(logTextBox); 119 | Controls.Add(cancelButton); 120 | Controls.Add(retryButton); 121 | Controls.Add(acceptButton); 122 | Controls.Add(userProgressBar); 123 | Controls.Add(userInfoLabel); 124 | DoubleBuffered = true; 125 | FormBorderStyle = FormBorderStyle.FixedSingle; 126 | MaximizeBox = false; 127 | MinimizeBox = false; 128 | Name = "InstallForm"; 129 | StartPosition = FormStartPosition.Manual; 130 | Text = "InstallForm"; 131 | Load += OnLoad; 132 | ResumeLayout(false); 133 | } 134 | 135 | #endregion 136 | 137 | private ProgressBar userProgressBar; 138 | private Label userInfoLabel; 139 | private Button acceptButton; 140 | private Button retryButton; 141 | private Button cancelButton; 142 | private RichTextBox logTextBox; 143 | private Button reselectButton; 144 | } 145 | } 146 | 147 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/InstallForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | text/microsoft-resx 111 | 112 | 113 | 2.0 114 | 115 | 116 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 117 | PublicKeyToken=b77a5c561934e089 118 | 119 | 120 | 121 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 122 | PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/SelectDialogForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | using CreamInstaller.Components; 6 | using CreamInstaller.Utility; 7 | 8 | namespace CreamInstaller.Forms; 9 | 10 | internal sealed partial class SelectDialogForm : CustomForm 11 | { 12 | private readonly List<(Platform platform, string id, string name)> selected = new(); 13 | 14 | internal SelectDialogForm(IWin32Window owner) : base(owner) 15 | { 16 | InitializeComponent(); 17 | selectionTreeView.TreeViewNodeSorter = PlatformIdComparer.NodeName; 18 | } 19 | 20 | internal DialogResult QueryUser(string groupBoxText, 21 | List<(Platform platform, string id, string name, bool alreadySelected)> potentialChoices, 22 | out List<(Platform platform, string id, string name)> choices) 23 | { 24 | choices = null; 25 | if (potentialChoices.Count < 1) 26 | return DialogResult.Cancel; 27 | groupBox.Text = groupBoxText; 28 | allCheckBox.Enabled = false; 29 | acceptButton.Enabled = false; 30 | selectionTreeView.AfterCheck += OnTreeNodeChecked; 31 | foreach ((Platform platform, string id, string name, bool alreadySelected) in potentialChoices) 32 | { 33 | TreeNode node = new() { Tag = platform, Name = id, Text = name, Checked = alreadySelected }; 34 | OnTreeNodeChecked(node); 35 | _ = selectionTreeView.Nodes.Add(node); 36 | } 37 | 38 | if (selected.Count < 1) 39 | OnLoad(null, null); 40 | allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; 41 | allCheckBox.Checked = selectionTreeView.Nodes.Cast().All(n => n.Checked); 42 | allCheckBox.CheckedChanged += OnAllCheckBoxChanged; 43 | allCheckBox.Enabled = true; 44 | acceptButton.Enabled = selected.Count > 0; 45 | saveButton.Enabled = acceptButton.Enabled; 46 | loadButton.Enabled = ProgramData.ReadProgramChoices() is not null; 47 | OnResize(null, null); 48 | Resize += OnResize; 49 | choices = selected; 50 | return ShowDialog(); 51 | } 52 | 53 | private void OnTreeNodeChecked(object sender, TreeViewEventArgs e) 54 | { 55 | OnTreeNodeChecked(e.Node); 56 | acceptButton.Enabled = selected.Count > 0; 57 | saveButton.Enabled = acceptButton.Enabled; 58 | } 59 | 60 | private void OnTreeNodeChecked(TreeNode node) 61 | { 62 | string id = node.Name; 63 | Platform platform = (Platform)node.Tag; 64 | if (node.Checked) 65 | selected.Add((platform, id, node.Text)); 66 | else 67 | _ = selected.RemoveAll(s => s.platform == platform && s.id == id); 68 | allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; 69 | allCheckBox.Checked = selectionTreeView.Nodes.Cast().All(n => n.Checked); 70 | allCheckBox.CheckedChanged += OnAllCheckBoxChanged; 71 | } 72 | 73 | private void OnResize(object s, EventArgs e) 74 | => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 75 | ? Program.ApplicationNameShort 76 | : Program.ApplicationName; 77 | 78 | private void OnSortCheckBoxChanged(object sender, EventArgs e) 79 | => selectionTreeView.TreeViewNodeSorter = 80 | sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; 81 | 82 | private void OnAllCheckBoxChanged(object sender, EventArgs e) 83 | { 84 | bool shouldCheck = selectionTreeView.Nodes.Cast().Any(n => !n.Checked); 85 | foreach (TreeNode node in selectionTreeView.Nodes) 86 | { 87 | node.Checked = shouldCheck; 88 | OnTreeNodeChecked(node); 89 | } 90 | 91 | allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; 92 | allCheckBox.Checked = shouldCheck; 93 | allCheckBox.CheckedChanged += OnAllCheckBoxChanged; 94 | } 95 | 96 | private void OnLoad(object sender, EventArgs e) 97 | { 98 | List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices().ToList(); 99 | if (choices.Count < 1) 100 | return; 101 | foreach (TreeNode node in selectionTreeView.Nodes) 102 | { 103 | node.Checked = choices.Any(n => n.platform == (Platform)node.Tag && n.id == node.Name); 104 | OnTreeNodeChecked(node); 105 | } 106 | } 107 | 108 | private void OnSave(object sender, EventArgs e) 109 | { 110 | ProgramData.WriteProgramChoices(selectionTreeView.Nodes.Cast().Where(n => n.Checked) 111 | .Select(node => ((Platform)node.Tag, node.Name))); 112 | loadButton.Enabled = ProgramData.ReadProgramChoices() is not null; 113 | } 114 | } -------------------------------------------------------------------------------- /CreamInstaller/Forms/SelectDialogForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | text/microsoft-resx 111 | 112 | 113 | 2.0 114 | 115 | 116 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 117 | PublicKeyToken=b77a5c561934e089 118 | 119 | 120 | 121 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 122 | PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/SelectForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 116 | PublicKeyToken=b77a5c561934e089 117 | 118 | 119 | 120 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 121 | PublicKeyToken=b77a5c561934e089 122 | 123 | 124 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/UpdateForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Forms; 3 | 4 | using CreamInstaller.Components; 5 | 6 | namespace CreamInstaller.Forms 7 | { 8 | partial class UpdateForm 9 | { 10 | private IContainer components = null; 11 | 12 | #region Windows Form Designer generated code 13 | 14 | /// 15 | /// Required method for Designer support - do not modify 16 | /// the contents of this method with the code editor. 17 | /// 18 | private void InitializeComponent() 19 | { 20 | progressLabel = new Label(); 21 | updateButton = new Button(); 22 | ignoreButton = new Button(); 23 | progressBar = new ProgressBar(); 24 | changelogTreeView = new CustomTreeView(); 25 | SuspendLayout(); 26 | // 27 | // progressLabel 28 | // 29 | progressLabel.Location = new System.Drawing.Point(12, 16); 30 | progressLabel.Margin = new Padding(3, 0, 3, 12); 31 | progressLabel.Name = "progressLabel"; 32 | progressLabel.Size = new System.Drawing.Size(218, 15); 33 | progressLabel.TabIndex = 0; 34 | progressLabel.Text = "Checking for updates . . ."; 35 | // 36 | // updateButton 37 | // 38 | updateButton.Anchor = AnchorStyles.Top | AnchorStyles.Right; 39 | updateButton.Enabled = false; 40 | updateButton.Location = new System.Drawing.Point(317, 12); 41 | updateButton.Margin = new Padding(3, 3, 3, 12); 42 | updateButton.Name = "updateButton"; 43 | updateButton.Size = new System.Drawing.Size(75, 23); 44 | updateButton.TabIndex = 2; 45 | updateButton.Text = "Update"; 46 | updateButton.UseVisualStyleBackColor = true; 47 | // 48 | // ignoreButton 49 | // 50 | ignoreButton.Anchor = AnchorStyles.Top | AnchorStyles.Right; 51 | ignoreButton.Enabled = false; 52 | ignoreButton.Location = new System.Drawing.Point(236, 12); 53 | ignoreButton.Margin = new Padding(3, 3, 3, 12); 54 | ignoreButton.Name = "ignoreButton"; 55 | ignoreButton.Size = new System.Drawing.Size(75, 23); 56 | ignoreButton.TabIndex = 1; 57 | ignoreButton.Text = "忽略"; 58 | ignoreButton.UseVisualStyleBackColor = true; 59 | ignoreButton.Click += OnIgnore; 60 | // 61 | // progressBar 62 | // 63 | progressBar.Location = new System.Drawing.Point(12, 41); 64 | progressBar.Name = "progressBar"; 65 | progressBar.Size = new System.Drawing.Size(380, 23); 66 | progressBar.TabIndex = 4; 67 | progressBar.Visible = false; 68 | // 69 | // changelogTreeView 70 | // 71 | changelogTreeView.Location = new System.Drawing.Point(12, 70); 72 | changelogTreeView.Margin = new Padding(0, 0, 0, 12); 73 | changelogTreeView.Name = "changelogTreeView"; 74 | changelogTreeView.Size = new System.Drawing.Size(380, 179); 75 | changelogTreeView.TabIndex = 5; 76 | // 77 | // UpdateForm 78 | // 79 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 80 | AutoScaleMode = AutoScaleMode.Font; 81 | AutoSize = true; 82 | AutoSizeMode = AutoSizeMode.GrowAndShrink; 83 | ClientSize = new System.Drawing.Size(404, 261); 84 | Controls.Add(changelogTreeView); 85 | Controls.Add(progressBar); 86 | Controls.Add(ignoreButton); 87 | Controls.Add(updateButton); 88 | Controls.Add(progressLabel); 89 | DoubleBuffered = true; 90 | FormBorderStyle = FormBorderStyle.FixedSingle; 91 | MaximizeBox = false; 92 | MinimizeBox = false; 93 | Name = "UpdateForm"; 94 | StartPosition = FormStartPosition.CenterScreen; 95 | Text = "UpdateForm"; 96 | Load += OnLoad; 97 | ResumeLayout(false); 98 | } 99 | 100 | #endregion 101 | 102 | private Label progressLabel; 103 | private Button updateButton; 104 | private Button ignoreButton; 105 | private ProgressBar progressBar; 106 | private CustomTreeView changelogTreeView; 107 | } 108 | } 109 | 110 | -------------------------------------------------------------------------------- /CreamInstaller/Forms/UpdateForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | text/microsoft-resx 111 | 112 | 113 | 2.0 114 | 115 | 116 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 117 | PublicKeyToken=b77a5c561934e089 118 | 119 | 120 | 121 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 122 | PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/EpicLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CreamInstaller.Platforms.Epic.Heroic; 6 | using CreamInstaller.Utility; 7 | using Microsoft.Win32; 8 | using Newtonsoft.Json; 9 | 10 | namespace CreamInstaller.Platforms.Epic; 11 | 12 | internal static class EpicLibrary 13 | { 14 | private static string epicManifestsPath; 15 | 16 | internal static string EpicManifestsPath 17 | { 18 | get 19 | { 20 | epicManifestsPath ??= 21 | Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir", null) as string; 22 | epicManifestsPath ??= 23 | Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Epic Games\EpicGamesLauncher", "AppDataPath", 24 | null) as string; 25 | if (epicManifestsPath is not null && epicManifestsPath.EndsWith(@"\Data", StringComparison.Ordinal)) 26 | epicManifestsPath += @"\Manifests"; 27 | return epicManifestsPath.ResolvePath(); 28 | } 29 | } 30 | 31 | internal static async Task> GetGames() 32 | => await Task.Run(async () => 33 | { 34 | List games = new(); 35 | string manifests = EpicManifestsPath; 36 | if (manifests.DirectoryExists()) 37 | foreach (string item in manifests.EnumerateDirectory("*.item")) 38 | { 39 | if (Program.Canceled) 40 | return games; 41 | string json = item.ReadFile(); 42 | try 43 | { 44 | Manifest manifest = JsonConvert.DeserializeObject(json); 45 | if (manifest is not null && (manifest.InstallLocation = manifest.InstallLocation.ResolvePath()) 46 | is not null 47 | && games.All(g => g.CatalogNamespace != manifest.CatalogNamespace)) 48 | games.Add(manifest); 49 | } 50 | catch 51 | { 52 | // ignored 53 | } 54 | } 55 | 56 | if (Program.Canceled) 57 | return games; 58 | await HeroicLibrary.GetGames(games); 59 | return games; 60 | }); 61 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/EpicStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using CreamInstaller.Platforms.Epic.GraphQL; 8 | using CreamInstaller.Utility; 9 | using Newtonsoft.Json; 10 | 11 | namespace CreamInstaller.Platforms.Epic; 12 | 13 | internal static class EpicStore 14 | { 15 | private const int Cooldown = 600; 16 | 17 | internal static async Task> 18 | QueryCatalog(string categoryNamespace) 19 | { 20 | List<(string id, string name, string product, string icon, string developer)> dlcIds = []; 21 | string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json"; 22 | bool cachedExists = cacheFile.FileExists(); 23 | Response response = null; 24 | if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, Cooldown)) 25 | { 26 | response = await QueryGraphQL(categoryNamespace); 27 | try 28 | { 29 | cacheFile.WriteFile(JsonConvert.SerializeObject(response, Formatting.Indented)); 30 | } 31 | catch 32 | { 33 | // ignored 34 | } 35 | } 36 | else 37 | try 38 | { 39 | response = JsonConvert.DeserializeObject(cacheFile.ReadFile()); 40 | } 41 | catch 42 | { 43 | cacheFile.DeleteFile(); 44 | } 45 | 46 | if (response is null) 47 | return dlcIds; 48 | List searchStore = [..response.Data.Catalog.SearchStore.Elements]; 49 | foreach (Element element in searchStore) 50 | { 51 | string title = element.Title; 52 | string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 53 | ? element.CatalogNs.Mappings.First().PageSlug 54 | : null; 55 | string icon = null; 56 | for (int i = 0; i < element.KeyImages?.Length; i++) 57 | { 58 | KeyImage keyImage = element.KeyImages[i]; 59 | if (keyImage.Type != "DieselStoreFront") 60 | continue; 61 | icon = keyImage.Url.ToString(); 62 | break; 63 | } 64 | 65 | foreach (Item item in element.Items) 66 | dlcIds.Populate(item.Id, title, product, icon, null, element.Items.Length == 1); 67 | } 68 | 69 | List catalogOffers = [..response.Data.Catalog.CatalogOffers.Elements]; 70 | foreach (Element element in catalogOffers) 71 | { 72 | string title = element.Title; 73 | string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 74 | ? element.CatalogNs.Mappings.First().PageSlug 75 | : null; 76 | string icon = null; 77 | for (int i = 0; i < element.KeyImages?.Length; i++) 78 | { 79 | KeyImage keyImage = element.KeyImages[i]; 80 | if (keyImage.Type != "Thumbnail") 81 | continue; 82 | icon = keyImage.Url.ToString(); 83 | break; 84 | } 85 | 86 | foreach (Item item in element.Items) 87 | dlcIds.Populate(item.Id, title, product, icon, item.Developer, element.Items.Length == 1); 88 | } 89 | 90 | return dlcIds; 91 | } 92 | 93 | private static void Populate( 94 | this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, 95 | string title, 96 | string product, string icon, string developer, bool canOverwrite = false) 97 | { 98 | if (id == null) 99 | return; 100 | bool found = false; 101 | for (int i = 0; i < dlcIds.Count; i++) 102 | { 103 | (string id, string name, string product, string icon, string developer) app = dlcIds[i]; 104 | if (app.id != id) 105 | continue; 106 | found = true; 107 | dlcIds[i] = canOverwrite 108 | ? (app.id, title ?? app.name, product ?? app.product, icon ?? app.icon, developer ?? app.developer) 109 | : (app.id, app.name ?? title, app.product ?? product, app.icon ?? icon, app.developer ?? developer); 110 | break; 111 | } 112 | 113 | if (!found) 114 | dlcIds.Add((id, title, product, icon, developer)); 115 | } 116 | 117 | private static async Task QueryGraphQL(string categoryNamespace) 118 | { 119 | try 120 | { 121 | string encoded = HttpUtility.UrlEncode(categoryNamespace); 122 | Request request = new(encoded); 123 | string payload = JsonConvert.SerializeObject(request); 124 | using HttpContent content = new StringContent(payload); 125 | content.Headers.ContentType = new("application/json"); 126 | HttpClient client = HttpClientManager.HttpClient; 127 | if (client is null) 128 | return null; 129 | HttpResponseMessage httpResponse = 130 | await client.PostAsync(new Uri("https://graphql.epicgames.com/graphql"), content); 131 | _ = httpResponse.EnsureSuccessStatusCode(); 132 | string response = await httpResponse.Content.ReadAsStringAsync(); 133 | return JsonConvert.DeserializeObject(response); 134 | } 135 | catch 136 | { 137 | return null; 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/GraphQL/Request.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0051 // Remove unused private members 2 | #pragma warning disable IDE0052 // Remove unread private members 3 | #pragma warning disable CA1812 // Avoid uninstantiated internal classes 4 | #pragma warning disable CA1822 // Mark members as static 5 | 6 | using Newtonsoft.Json; 7 | 8 | namespace CreamInstaller.Platforms.Epic.GraphQL; 9 | 10 | internal sealed class Request 11 | { 12 | internal Request(string @namespace) => Vars = new(@namespace); 13 | 14 | [JsonProperty(PropertyName = "query")] 15 | private string Query 16 | => """ 17 | query searchOffers($namespace: String!) { 18 | Catalog { 19 | searchStore(category: "*", namespace: $namespace){ 20 | elements { 21 | id 22 | title 23 | developer 24 | items { 25 | id 26 | } 27 | catalogNs { 28 | mappings(pageType: "productHome") { 29 | pageSlug 30 | } 31 | } 32 | } 33 | } 34 | catalogOffers( 35 | namespace: $namespace 36 | params: { 37 | count: 1000, 38 | } 39 | ) { 40 | elements { 41 | id 42 | title 43 | keyImages { 44 | type 45 | url 46 | } 47 | items { 48 | id 49 | title 50 | developer 51 | } 52 | catalogNs { 53 | mappings(pageType: "productHome") { 54 | pageSlug 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | """; 62 | 63 | [JsonProperty(PropertyName = "variables")] 64 | private Variables Vars { get; set; } 65 | 66 | private sealed class Headers 67 | { 68 | [JsonProperty(PropertyName = "Content-Type")] 69 | private string ContentType => "application/graphql"; 70 | } 71 | 72 | private sealed class Variables 73 | { 74 | internal Variables(string @namespace) => Namespace = @namespace; 75 | 76 | [JsonProperty(PropertyName = "namespace")] 77 | private string Namespace { get; set; } 78 | } 79 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/GraphQL/Response.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace CreamInstaller.Platforms.Epic.GraphQL; 5 | 6 | public class Response 7 | { 8 | [JsonProperty(PropertyName = "data")] public ResponseData Data { get; protected set; } 9 | } 10 | 11 | public class ResponseData 12 | { 13 | [JsonProperty(PropertyName = "Catalog")] 14 | public Catalog Catalog { get; protected set; } 15 | } 16 | 17 | public class Catalog 18 | { 19 | [JsonProperty(PropertyName = "searchStore")] 20 | public ElementContainer SearchStore { get; protected set; } 21 | 22 | [JsonProperty(PropertyName = "catalogOffers")] 23 | public ElementContainer CatalogOffers { get; protected set; } 24 | } 25 | 26 | public class ElementContainer 27 | { 28 | [JsonProperty(PropertyName = "elements")] 29 | public Element[] Elements { get; protected set; } 30 | } 31 | 32 | public class Element 33 | { 34 | [JsonProperty(PropertyName = "id")] public string Id { get; protected set; } 35 | 36 | [JsonProperty(PropertyName = "title")] public string Title { get; protected set; } 37 | 38 | [JsonProperty(PropertyName = "keyImages")] 39 | public KeyImage[] KeyImages { get; protected set; } 40 | 41 | [JsonProperty(PropertyName = "items")] public Item[] Items { get; protected set; } 42 | 43 | [JsonProperty(PropertyName = "catalogNs")] 44 | public CatalogNs CatalogNs { get; protected set; } 45 | } 46 | 47 | public class Item 48 | { 49 | [JsonProperty(PropertyName = "id")] public string Id { get; protected set; } 50 | 51 | [JsonProperty(PropertyName = "title")] public string Title { get; protected set; } 52 | 53 | [JsonProperty(PropertyName = "developer")] 54 | public string Developer { get; protected set; } 55 | } 56 | 57 | public class KeyImage 58 | { 59 | [JsonProperty(PropertyName = "type")] public string Type { get; protected set; } 60 | 61 | [JsonProperty(PropertyName = "url")] public Uri Url { get; protected set; } 62 | } 63 | 64 | public class CatalogNs 65 | { 66 | [JsonProperty(PropertyName = "mappings")] 67 | public Mapping[] Mappings { get; protected set; } 68 | } 69 | 70 | public class Mapping 71 | { 72 | [JsonProperty(PropertyName = "pageSlug")] 73 | public string PageSlug { get; protected set; } 74 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/Heroic/HeroicAppData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace CreamInstaller.Platforms.Epic.Heroic; 4 | 5 | public class HeroicInstall 6 | { 7 | [JsonProperty("install_path")] public string InstallPath { get; set; } 8 | } 9 | 10 | public class HeroicAppData 11 | { 12 | [JsonProperty("install")] public HeroicInstall Install { get; set; } 13 | 14 | [JsonProperty("namespace")] public string Namespace { get; set; } 15 | 16 | [JsonProperty("title")] public string Title { get; set; } 17 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/Heroic/HeroicLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CreamInstaller.Utility; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace CreamInstaller.Platforms.Epic.Heroic; 9 | 10 | internal static class HeroicLibrary 11 | { 12 | internal static readonly string HeroicLibraryPath 13 | = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + 14 | @"\heroic\store_cache\legendary_library.json"; 15 | 16 | internal static async Task GetGames(List games) 17 | => await Task.Run(() => 18 | { 19 | string libraryPath = HeroicLibraryPath; 20 | if (!libraryPath.FileExists()) 21 | return; 22 | string libraryJson = libraryPath.ReadFile(); 23 | try 24 | { 25 | JObject library = JObject.Parse(libraryJson); 26 | if (!library.TryGetValue("library", out JToken libraryToken) || libraryToken is not JArray libraryArray) 27 | return; 28 | foreach (JToken token in libraryArray) 29 | try 30 | { 31 | HeroicAppData appData = token.ToObject(); 32 | if (appData is null || string.IsNullOrWhiteSpace(appData.Install.InstallPath = 33 | appData.Install.InstallPath.ResolvePath())) 34 | continue; 35 | Manifest manifest = new() 36 | { 37 | DisplayName = appData.Title, CatalogNamespace = appData.Namespace, 38 | InstallLocation = appData.Install.InstallPath 39 | }; 40 | if (games.All(g => g.CatalogNamespace != manifest.CatalogNamespace)) 41 | games.Add(manifest); 42 | } 43 | catch 44 | { 45 | // ignored 46 | } 47 | } 48 | catch 49 | { 50 | // ignored 51 | } 52 | }); 53 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Epic/Manifest.cs: -------------------------------------------------------------------------------- 1 | namespace CreamInstaller.Platforms.Epic; 2 | 3 | public class Manifest 4 | { 5 | public string DisplayName { get; set; } 6 | 7 | public string InstallLocation { get; set; } 8 | 9 | public string CatalogNamespace { get; set; } 10 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/AppDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace CreamInstaller.Platforms.Steam; 5 | 6 | public class AppFullGame 7 | { 8 | [JsonProperty(PropertyName = "appid")] public string AppId { get; set; } 9 | 10 | [JsonProperty(PropertyName = "name")] public string Name { get; set; } 11 | } 12 | 13 | public class AppData 14 | { 15 | [JsonProperty(PropertyName = "type")] public string Type { get; set; } 16 | 17 | [JsonProperty(PropertyName = "name")] public string Name { get; set; } 18 | 19 | [JsonProperty(PropertyName = "steam_appid")] 20 | public int SteamAppId { get; set; } 21 | 22 | [JsonProperty(PropertyName = "fullgame")] 23 | public AppFullGame FullGame { get; set; } 24 | 25 | [JsonProperty(PropertyName = "dlc")] public List DLC { get; set; } 26 | 27 | [JsonProperty(PropertyName = "header_image")] 28 | public string HeaderImage { get; set; } 29 | 30 | [JsonProperty(PropertyName = "website")] 31 | public string Website { get; set; } 32 | 33 | [JsonProperty(PropertyName = "developers")] 34 | public List Developers { get; set; } 35 | 36 | [JsonProperty(PropertyName = "publishers")] 37 | public List Publishers { get; set; } 38 | 39 | [JsonProperty(PropertyName = "packages")] 40 | public List Packages { get; set; } 41 | } 42 | 43 | public class AppDetails 44 | { 45 | [JsonProperty(PropertyName = "success")] 46 | public bool Success { get; set; } 47 | 48 | [JsonProperty(PropertyName = "data")] public AppData Data { get; set; } 49 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/CmdAppDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace CreamInstaller.Platforms.Steam; 5 | 6 | public class CmdAppCommon 7 | { 8 | [JsonProperty(PropertyName = "type")] public string Type { get; set; } 9 | 10 | [JsonProperty(PropertyName = "name")] public string Name { get; set; } 11 | 12 | [JsonProperty(PropertyName = "icon")] public string Icon { get; set; } 13 | 14 | [JsonProperty(PropertyName = "clienticon")] 15 | public string ClientIcon { get; set; } 16 | 17 | [JsonProperty(PropertyName = "logo_small")] 18 | public string LogoSmall { get; set; } 19 | 20 | [JsonProperty(PropertyName = "logo")] public string Logo { set; get; } 21 | 22 | [JsonProperty(PropertyName = "parent")] 23 | public string Parent { set; get; } 24 | } 25 | 26 | public class CmdAppExtended 27 | { 28 | [JsonProperty(PropertyName = "listofdlc")] 29 | public string Dlc { get; set; } 30 | 31 | [JsonProperty(PropertyName = "publisher")] 32 | public string Publisher { get; set; } 33 | } 34 | 35 | public class CmdAppData 36 | { 37 | [JsonProperty(PropertyName = "common")] 38 | public CmdAppCommon Common { get; set; } 39 | 40 | [JsonProperty(PropertyName = "depots")] 41 | public Dictionary Depots { get; set; } 42 | 43 | [JsonProperty(PropertyName = "extended")] 44 | public CmdAppExtended Extended { get; set; } 45 | } 46 | 47 | public class CmdAppDetails 48 | { 49 | [JsonProperty(PropertyName = "status")] 50 | public string Status { get; set; } 51 | 52 | [JsonProperty(PropertyName = "data")] public Dictionary Data { get; set; } 53 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/SteamCMD.WebAPI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using CreamInstaller.Forms; 6 | using CreamInstaller.Utility; 7 | using Newtonsoft.Json; 8 | 9 | namespace CreamInstaller.Platforms.Steam; 10 | 11 | internal static partial class SteamCMD 12 | { 13 | private const int CooldownGame = 600; 14 | private const int CooldownDlc = 1200; 15 | 16 | private static async Task QueryWebAPI(string appId, bool isDlc = false, int attempts = 0) 17 | { 18 | while (!Program.Canceled) 19 | { 20 | attempts++; 21 | string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.cmd.json"; 22 | bool cachedExists = cacheFile.FileExists(); 23 | if (!cachedExists || ProgramData.CheckCooldown(appId + ".cmd", isDlc ? CooldownDlc : CooldownGame)) 24 | { 25 | string response = 26 | await HttpClientManager.EnsureGet($"https://api.steamcmd.net/v1/info/{appId}"); 27 | if (response is not null) 28 | { 29 | try 30 | { 31 | CmdAppDetails appDetails = JsonConvert.DeserializeObject(response); 32 | if (appDetails is not null && appDetails.Status == "success") 33 | { 34 | if (appDetails.Data.Values.Count != 0) 35 | { 36 | CmdAppData data = appDetails.Data.Values.First(); 37 | try 38 | { 39 | cacheFile.WriteFile(JsonConvert.SerializeObject(data, Formatting.Indented)); 40 | } 41 | catch 42 | #if DEBUG 43 | (Exception e) 44 | { 45 | DebugForm.Current.Log("SteamCMD web API query failed on attempt #" + attempts + 46 | " for " + appId + (isDlc ? " (DLC)" : "") 47 | + ": Unsuccessful serialization (" + e.Message + ")"); 48 | } 49 | #else 50 | { 51 | // ignored 52 | } 53 | #endif 54 | return data; 55 | } 56 | #if DEBUG 57 | else 58 | DebugForm.Current.Log( 59 | "SteamCMD web API query failed on attempt #" + attempts + " for " + appId + 60 | (isDlc ? " (DLC)" : "") 61 | + ": No data", 62 | LogTextBox.Warning); 63 | #endif 64 | } 65 | #if DEBUG 66 | else 67 | DebugForm.Current.Log( 68 | "SteamCMD web API query failed on attempt #" + attempts + " for " + appId + 69 | (isDlc ? " (DLC)" : "") 70 | + ": Status not success (" + appDetails?.Status + ")", 71 | LogTextBox.Warning); 72 | #endif 73 | } 74 | catch 75 | #if DEBUG 76 | (Exception e) 77 | { 78 | DebugForm.Current.Log("SteamCMD web API query failed on attempt #" + attempts + " for " + 79 | appId + (isDlc ? " (DLC)" : "") 80 | + ": Unsuccessful deserialization (" + e.Message + ")"); 81 | } 82 | #else 83 | { 84 | // ignored 85 | } 86 | #endif 87 | } 88 | #if DEBUG 89 | else 90 | DebugForm.Current.Log( 91 | "SteamCMD web API query failed on attempt #" + attempts + " for " + appId + 92 | (isDlc ? " (DLC)" : "") + 93 | ": Response null", 94 | LogTextBox.Warning); 95 | #endif 96 | } 97 | 98 | if (cachedExists) 99 | try 100 | { 101 | return JsonConvert.DeserializeObject(cacheFile.ReadFile()); 102 | } 103 | catch 104 | { 105 | cacheFile.DeleteFile(); 106 | } 107 | 108 | if (isDlc) 109 | break; 110 | if (attempts > 10) 111 | { 112 | #if DEBUG 113 | DebugForm.Current.Log("Failed to query SteamCMD web API after 10 tries: " + appId); 114 | #endif 115 | break; 116 | } 117 | 118 | Thread.Sleep(1000); 119 | } 120 | 121 | return null; 122 | } 123 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/SteamLibrary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using CreamInstaller.Utility; 5 | using Gameloop.Vdf.Linq; 6 | using Microsoft.Win32; 7 | 8 | namespace CreamInstaller.Platforms.Steam; 9 | 10 | internal static class SteamLibrary 11 | { 12 | private static string installPath; 13 | 14 | internal static string InstallPath 15 | { 16 | get 17 | { 18 | installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Valve\Steam", "SteamPath", null) as string; 19 | installPath ??= 20 | Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", null) as string; 21 | return installPath.ResolvePath(); 22 | } 23 | } 24 | 25 | internal static async Task> 26 | GetGames() 27 | => await Task.Run(async () => 28 | { 29 | List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); 30 | HashSet gameLibraryDirectories = await GetLibraryDirectories(); 31 | foreach (string libraryDirectory in gameLibraryDirectories) 32 | { 33 | if (Program.Canceled) 34 | return games; 35 | foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in (await 36 | GetGamesFromLibraryDirectory( 37 | libraryDirectory)).Where(game => games.All(_game => _game.appId != game.appId))) 38 | games.Add(game); 39 | } 40 | 41 | return games; 42 | }); 43 | 44 | private static async Task> 45 | GetGamesFromLibraryDirectory(string libraryDirectory) 46 | => await Task.Run(() => 47 | { 48 | List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); 49 | if (Program.Canceled || !libraryDirectory.DirectoryExists()) 50 | return games; 51 | foreach (string file in libraryDirectory.EnumerateDirectory("*.acf")) 52 | { 53 | if (Program.Canceled) 54 | return games; 55 | if (!ValveDataFile.TryDeserialize(file.ReadFile(), out VProperty result)) 56 | continue; 57 | string appId = result.Value.GetChild("appid")?.ToString(); 58 | string installdir = result.Value.GetChild("installdir")?.ToString(); 59 | string name = result.Value.GetChild("name")?.ToString(); 60 | string buildId = result.Value.GetChild("buildid")?.ToString(); 61 | if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(installdir) || 62 | string.IsNullOrWhiteSpace(name) 63 | || string.IsNullOrWhiteSpace(buildId)) 64 | continue; 65 | string gameDirectory = (libraryDirectory + @"\common\" + installdir).ResolvePath(); 66 | if (gameDirectory is null || !int.TryParse(appId, out int _) || 67 | !int.TryParse(buildId, out int buildIdInt) || games.Any(g => g.appId == appId)) 68 | continue; 69 | VToken userConfig = result.Value.GetChild("UserConfig"); 70 | string branch = userConfig?.GetChild("BetaKey")?.ToString(); 71 | branch ??= userConfig?.GetChild("betakey")?.ToString(); 72 | if (branch is null) 73 | { 74 | VToken mountedConfig = result.Value.GetChild("MountedConfig"); 75 | branch = mountedConfig?.GetChild("BetaKey")?.ToString(); 76 | branch ??= mountedConfig?.GetChild("betakey")?.ToString(); 77 | } 78 | 79 | if (string.IsNullOrWhiteSpace(branch)) 80 | branch = "public"; 81 | games.Add((appId, name, branch, buildIdInt, gameDirectory)); 82 | } 83 | 84 | return games; 85 | }); 86 | 87 | private static async Task> GetLibraryDirectories() 88 | => await Task.Run(() => 89 | { 90 | HashSet libraryDirectories = new(); 91 | if (Program.Canceled) 92 | return libraryDirectories; 93 | string steamInstallPath = InstallPath; 94 | if (steamInstallPath == null || !steamInstallPath.DirectoryExists()) 95 | return libraryDirectories; 96 | string libraryFolder = steamInstallPath + @"\steamapps"; 97 | if (!libraryFolder.DirectoryExists()) 98 | return libraryDirectories; 99 | _ = libraryDirectories.Add(libraryFolder); 100 | string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; 101 | if (!libraryFolders.FileExists() || 102 | !ValveDataFile.TryDeserialize(libraryFolders.ReadFile(), out VProperty result)) 103 | return libraryDirectories; 104 | foreach (VToken vToken in result.Value.Where(p => 105 | p is VProperty property && int.TryParse(property.Key, out int _))) 106 | { 107 | VProperty property = (VProperty)vToken; 108 | string path = property.Value.GetChild("path")?.ToString(); 109 | if (string.IsNullOrWhiteSpace(path)) 110 | continue; 111 | path += @"\steamapps"; 112 | if (path.DirectoryExists()) 113 | _ = libraryDirectories.Add(path); 114 | } 115 | 116 | return libraryDirectories; 117 | }); 118 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/SteamStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using CreamInstaller.Utility; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | #if DEBUG 10 | using System; 11 | using CreamInstaller.Forms; 12 | #endif 13 | 14 | namespace CreamInstaller.Platforms.Steam; 15 | 16 | internal static class SteamStore 17 | { 18 | private const int CooldownGame = 600; 19 | private const int CooldownDlc = 1200; 20 | 21 | internal static async Task> ParseDlcAppIds(StoreAppData storeAppData) 22 | => await Task.Run(() => 23 | { 24 | HashSet dlcIds = new(); 25 | if (storeAppData.DLC is null) 26 | return dlcIds; 27 | foreach (string dlcId in from appId in storeAppData.DLC 28 | where appId > 0 29 | select appId.ToString(CultureInfo.InvariantCulture)) 30 | _ = dlcIds.Add(dlcId); 31 | return dlcIds; 32 | }); 33 | 34 | internal static async Task QueryStoreAPI(string appId, bool isDlc = false, int attempts = 0) 35 | { 36 | while (!Program.Canceled) 37 | { 38 | attempts++; 39 | string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json"; 40 | bool cachedExists = cacheFile.FileExists(); 41 | if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? CooldownDlc : CooldownGame)) 42 | { 43 | string response = 44 | await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); 45 | if (response is not null) 46 | { 47 | Dictionary apps = 48 | JsonConvert.DeserializeObject>(response); 49 | if (apps is not null) 50 | foreach (KeyValuePair app in apps) 51 | try 52 | { 53 | StoreAppDetails storeAppDetails = 54 | JsonConvert.DeserializeObject(app.Value.ToString()); 55 | if (storeAppDetails is not null) 56 | { 57 | StoreAppData data = storeAppDetails.Data; 58 | if (!storeAppDetails.Success) 59 | { 60 | #if DEBUG 61 | DebugForm.Current.Log( 62 | "Steam store query failed on attempt #" + attempts + " for " + appId + 63 | (isDlc ? " (DLC)" : "") 64 | + ": Query unsuccessful (" + app.Value.ToString(Formatting.None) + ")", 65 | LogTextBox.Warning); 66 | #endif 67 | if (data is null) 68 | return null; 69 | } 70 | 71 | if (data is not null) 72 | { 73 | try 74 | { 75 | cacheFile.WriteFile(JsonConvert.SerializeObject(data, Formatting.Indented)); 76 | } 77 | catch 78 | #if DEBUG 79 | (Exception e) 80 | { 81 | DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + 82 | " for " + appId + (isDlc ? " (DLC)" : "") 83 | + ": Unsuccessful serialization (" + e.Message + ")"); 84 | } 85 | #else 86 | { 87 | // ignored 88 | } 89 | #endif 90 | return data; 91 | } 92 | #if DEBUG 93 | DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + 94 | appId + (isDlc ? " (DLC)" : "") 95 | + ": Response data null (" + 96 | app.Value.ToString(Formatting.None) + ")"); 97 | #endif 98 | } 99 | #if DEBUG 100 | else 101 | DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + 102 | appId + (isDlc ? " (DLC)" : "") 103 | + ": Response details null (" + 104 | app.Value.ToString(Formatting.None) + ")"); 105 | #endif 106 | } 107 | catch 108 | #if DEBUG 109 | (Exception e) 110 | { 111 | DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + 112 | appId + (isDlc ? " (DLC)" : "") 113 | + ": Unsuccessful deserialization (" + e.Message + ")"); 114 | } 115 | #else 116 | { 117 | // ignored 118 | } 119 | #endif 120 | #if DEBUG 121 | else 122 | DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + 123 | (isDlc ? " (DLC)" : "") 124 | + ": Response deserialization null"); 125 | #endif 126 | } 127 | #if DEBUG 128 | else 129 | DebugForm.Current.Log( 130 | "Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") + 131 | ": Response null", 132 | LogTextBox.Warning); 133 | #endif 134 | } 135 | 136 | if (cachedExists) 137 | try 138 | { 139 | return JsonConvert.DeserializeObject(cacheFile.ReadFile()); 140 | } 141 | catch 142 | { 143 | cacheFile.DeleteFile(); 144 | } 145 | 146 | if (isDlc) 147 | break; 148 | if (attempts > 10) 149 | { 150 | #if DEBUG 151 | DebugForm.Current.Log("Failed to query Steam store after 10 tries: " + appId); 152 | #endif 153 | break; 154 | } 155 | 156 | Thread.Sleep(1000); 157 | } 158 | 159 | return null; 160 | } 161 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/StoreAppDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace CreamInstaller.Platforms.Steam; 5 | 6 | public class StoreAppFullGame 7 | { 8 | [JsonProperty(PropertyName = "appid")] public string AppId { get; set; } 9 | 10 | [JsonProperty(PropertyName = "name")] public string Name { get; set; } 11 | } 12 | 13 | public class StoreAppData 14 | { 15 | [JsonProperty(PropertyName = "type")] public string Type { get; set; } 16 | 17 | [JsonProperty(PropertyName = "name")] public string Name { get; set; } 18 | 19 | [JsonProperty(PropertyName = "steam_appid")] 20 | public int SteamAppId { get; set; } 21 | 22 | [JsonProperty(PropertyName = "fullgame")] 23 | public StoreAppFullGame FullGame { get; set; } 24 | 25 | [JsonProperty(PropertyName = "dlc")] public List DLC { get; set; } 26 | 27 | [JsonProperty(PropertyName = "header_image")] 28 | public string HeaderImage { get; set; } 29 | 30 | [JsonProperty(PropertyName = "website")] 31 | public string Website { get; set; } 32 | 33 | [JsonProperty(PropertyName = "developers")] 34 | public List Developers { get; set; } 35 | 36 | [JsonProperty(PropertyName = "publishers")] 37 | public List Publishers { get; set; } 38 | 39 | [JsonProperty(PropertyName = "packages")] 40 | public List Packages { get; set; } 41 | } 42 | 43 | public class StoreAppDetails 44 | { 45 | [JsonProperty(PropertyName = "success")] 46 | public bool Success { get; set; } 47 | 48 | [JsonProperty(PropertyName = "data")] public StoreAppData Data { get; set; } 49 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Steam/ValveDataFile.cs: -------------------------------------------------------------------------------- 1 | using Gameloop.Vdf; 2 | using Gameloop.Vdf.Linq; 3 | 4 | namespace CreamInstaller.Platforms.Steam; 5 | 6 | internal static class ValveDataFile 7 | { 8 | internal static bool TryDeserialize(string value, out VProperty result) 9 | { 10 | result = null; 11 | try 12 | { 13 | result = VdfConvert.Deserialize(value); 14 | return true; 15 | } 16 | catch 17 | { 18 | // ignored 19 | } 20 | 21 | return false; 22 | } 23 | 24 | internal static VToken GetChild(this VToken token, string index) 25 | { 26 | try 27 | { 28 | return token[index]; 29 | } 30 | catch 31 | { 32 | // ignored 33 | } 34 | 35 | return null; 36 | } 37 | } -------------------------------------------------------------------------------- /CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CreamInstaller.Utility; 6 | using Microsoft.Win32; 7 | 8 | namespace CreamInstaller.Platforms.Ubisoft; 9 | 10 | internal static class UbisoftLibrary 11 | { 12 | private static RegistryKey installsKey; 13 | 14 | private static RegistryKey InstallsKey 15 | { 16 | get 17 | { 18 | installsKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Ubisoft\Launcher\Installs"); 19 | return installsKey; 20 | } 21 | } 22 | 23 | internal static async Task> GetGames() 24 | => await Task.Run(() => 25 | { 26 | List<(string gameId, string name, string gameDirectory)> games = new(); 27 | RegistryKey installsKey = InstallsKey; 28 | if (installsKey is null) 29 | return games; 30 | foreach (string gameId in installsKey.GetSubKeyNames()) 31 | { 32 | RegistryKey installKey = installsKey.OpenSubKey(gameId); 33 | string installDir = installKey?.GetValue("InstallDir")?.ToString()?.ResolvePath(); 34 | if (installDir is not null && games.All(g => g.gameId != gameId)) 35 | games.Add((gameId, new DirectoryInfo(installDir).Name, installDir)); 36 | } 37 | 38 | return games; 39 | }); 40 | } -------------------------------------------------------------------------------- /CreamInstaller/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Windows.Forms; 6 | using CreamInstaller.Forms; 7 | using CreamInstaller.Platforms.Steam; 8 | using CreamInstaller.Utility; 9 | 10 | namespace CreamInstaller; 11 | 12 | internal static class Program 13 | { 14 | internal static readonly string Name = Application.CompanyName; 15 | private static readonly string Description = Application.ProductName; 16 | 17 | internal static readonly string Version = Application.ProductVersion[ 18 | ..(Application.ProductVersion.IndexOf('+') is var index && index != -1 19 | ? index 20 | : Application.ProductVersion.Length)]; 21 | 22 | internal const string RepositoryOwner = "pointfeev"; 23 | internal static readonly string RepositoryName = Name; 24 | internal static readonly string RepositoryPackage = Name + ".zip"; 25 | internal static readonly string RepositoryExecutable = Name + ".exe"; 26 | #if DEBUG 27 | internal static readonly string ApplicationName = Name + " v" + Version + "-debug: " + Description; 28 | internal static readonly string ApplicationNameShort = Name + " v" + Version + "-debug"; 29 | #else 30 | internal static readonly string ApplicationName = Name + " v" + Version + ": " + Description; 31 | internal static readonly string ApplicationNameShort = Name + " v" + Version; 32 | #endif 33 | 34 | private static readonly Process CurrentProcess = Process.GetCurrentProcess(); 35 | internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule?.FileName; 36 | internal static readonly int CurrentProcessId = CurrentProcess.Id; 37 | 38 | // this may forever be false, but who knows, maybe acidicoala makes it once again better than CreamAPI some day 39 | internal const bool UseSmokeAPI = false; 40 | 41 | internal static bool BlockProtectedGames = true; 42 | internal static readonly string[] ProtectedGames = ["PAYDAY 2"]; 43 | internal static readonly string[] ProtectedGameDirectories = [@"\EasyAntiCheat", @"\BattlEye"]; 44 | internal static readonly string[] ProtectedGameDirectoryExceptions = []; 45 | 46 | internal static bool IsGameBlocked(string name, string directory = null) 47 | => BlockProtectedGames && (ProtectedGames.Contains(name) || directory is not null && 48 | !ProtectedGameDirectoryExceptions.Contains(name) 49 | && ProtectedGameDirectories.Any(path => (directory + path).DirectoryExists())); 50 | 51 | [STAThread] 52 | private static void Main() 53 | { 54 | using Mutex mutex = new(true, Name, out bool createdNew); 55 | if (createdNew) 56 | { 57 | _ = Application.SetHighDpiMode(HighDpiMode.SystemAware); 58 | Application.EnableVisualStyles(); 59 | Application.SetCompatibleTextRenderingDefault(false); 60 | Application.ApplicationExit += OnApplicationExit; 61 | Application.ThreadException += (_, e) => e.Exception.HandleFatalException(); 62 | Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 63 | AppDomain.CurrentDomain.UnhandledException += 64 | (_, e) => (e.ExceptionObject as Exception)?.HandleFatalException(); 65 | retry: 66 | try 67 | { 68 | HttpClientManager.Setup(); 69 | using UpdateForm form = new(); 70 | #if DEBUG 71 | DebugForm.Current.Attach(form); 72 | #endif 73 | Application.Run(form); 74 | } 75 | catch (Exception e) 76 | { 77 | if (e.HandleException()) 78 | goto retry; 79 | Application.Exit(); 80 | return; 81 | } 82 | } 83 | 84 | mutex.Close(); 85 | } 86 | 87 | internal static bool Canceled; 88 | 89 | internal static async void Cleanup(bool cancel = true) 90 | { 91 | Canceled = cancel; 92 | await SteamCMD.Cleanup(); 93 | } 94 | 95 | private static void OnApplicationExit(object s, EventArgs e) 96 | { 97 | Cleanup(); 98 | HttpClientManager.Dispose(); 99 | } 100 | } -------------------------------------------------------------------------------- /CreamInstaller/ProgramRelease.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Newtonsoft.Json; 5 | 6 | namespace CreamInstaller; 7 | 8 | public class ProgramRelease 9 | { 10 | private Asset asset; 11 | 12 | private string[] changes; 13 | 14 | private Version version; 15 | 16 | [JsonProperty("tag_name", NullValueHandling = NullValueHandling.Ignore)] 17 | public string TagName { get; set; } 18 | 19 | [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] 20 | public string Name { get; set; } 21 | 22 | [JsonProperty("draft", NullValueHandling = NullValueHandling.Ignore)] 23 | public bool Draft { get; set; } 24 | 25 | [JsonProperty("prerelease", NullValueHandling = NullValueHandling.Ignore)] 26 | public bool Prerelease { get; set; } 27 | 28 | [JsonProperty("assets", NullValueHandling = NullValueHandling.Ignore)] 29 | public List Assets { get; } = new(); 30 | 31 | [JsonProperty("body", NullValueHandling = NullValueHandling.Ignore)] 32 | public string Body { get; set; } 33 | 34 | public Asset Asset => asset ??= Assets.FirstOrDefault(a => a.Name == Program.RepositoryPackage); 35 | 36 | public Version Version => version ??= new(TagName[1..]); 37 | 38 | public string[] Changes => changes ??= Body.Replace("- ", "").Split("\r\n"); 39 | } 40 | 41 | public class Asset 42 | { 43 | [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] 44 | public string Name { get; set; } 45 | 46 | [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)] 47 | public int Size { get; set; } 48 | 49 | [JsonProperty("browser_download_url", NullValueHandling = NullValueHandling.Ignore)] 50 | public string BrowserDownloadUrl { get; set; } 51 | } -------------------------------------------------------------------------------- /CreamInstaller/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Resources; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: ComVisible(false)] 6 | [assembly: CLSCompliant(true)] 7 | [assembly: NeutralResourcesLanguage("en")] -------------------------------------------------------------------------------- /CreamInstaller/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace CreamInstaller.Properties { 11 | using System; 12 | 13 | 14 | /// 15 | /// A strongly-typed resource class, for looking up localized strings, etc. 16 | /// 17 | // This class was auto-generated by the StronglyTypedResourceBuilder 18 | // class via a tool like ResGen or Visual Studio. 19 | // To add or remove a member, edit your .ResX file then rerun ResGen 20 | // with the /str option, or rebuild your VS project. 21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 24 | internal class Resources { 25 | 26 | private static global::System.Resources.ResourceManager resourceMan; 27 | 28 | private static global::System.Globalization.CultureInfo resourceCulture; 29 | 30 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 31 | internal Resources() { 32 | } 33 | 34 | /// 35 | /// Returns the cached ResourceManager instance used by this class. 36 | /// 37 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 38 | internal static global::System.Resources.ResourceManager ResourceManager { 39 | get { 40 | if (object.ReferenceEquals(resourceMan, null)) { 41 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CreamInstaller.Properties.Resources", typeof(Resources).Assembly); 42 | resourceMan = temp; 43 | } 44 | return resourceMan; 45 | } 46 | } 47 | 48 | /// 49 | /// Overrides the current thread's CurrentUICulture property for all 50 | /// resource lookups using this strongly typed resource class. 51 | /// 52 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 53 | internal static global::System.Globalization.CultureInfo Culture { 54 | get { 55 | return resourceCulture; 56 | } 57 | set { 58 | resourceCulture = value; 59 | } 60 | } 61 | 62 | /// 63 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 64 | /// 65 | internal static System.Drawing.Icon Icon { 66 | get { 67 | object obj = ResourceManager.GetObject("Icon", resourceCulture); 68 | return ((System.Drawing.Icon)(obj)); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /CreamInstaller/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | text/microsoft-resx 111 | 112 | 113 | 2.0 114 | 115 | 116 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 117 | PublicKeyToken=b77a5c561934e089 118 | 119 | 120 | 121 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, 122 | PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | 127 | 128 | ..\resources\program.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, 129 | PublicKeyToken=b03f5f7f11d50a3a 130 | 131 | 132 | -------------------------------------------------------------------------------- /CreamInstaller/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "CreamInstaller": { 4 | "commandName": "Project", 5 | "hotReloadEnabled": false, 6 | "nativeDebugging": false 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /CreamInstaller/Resources/CreamAPI/steam_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/CreamAPI/steam_api.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/CreamAPI/steam_api64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/CreamAPI/steam_api64.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/audioses-32/audioses.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/audioses-32/audioses.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/audioses-64/audioses.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/audioses-64/audioses.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/d3d10-32/d3d10.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/d3d10-32/d3d10.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/d3d10-64/d3d10.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/d3d10-64/d3d10.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/d3d11-32/d3d11.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/d3d11-32/d3d11.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/d3d11-64/d3d11.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/d3d11-64/d3d11.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/d3d9-32/d3d9.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/d3d9-32/d3d9.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/d3d9-64/d3d9.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/d3d9-64/d3d9.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/dinput8-32/dinput8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/dinput8-32/dinput8.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/dinput8-64/dinput8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/dinput8-64/dinput8.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/dwmapi-32/dwmapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/dwmapi-32/dwmapi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/dwmapi-64/dwmapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/dwmapi-64/dwmapi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/dxgi-32/dxgi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/dxgi-32/dxgi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/dxgi-64/dxgi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/dxgi-64/dxgi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/glu32-32/glu32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/glu32-32/glu32.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/glu32-64/glu32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/glu32-64/glu32.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/hid-32/hid.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/hid-32/hid.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/hid-64/hid.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/hid-64/hid.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/iphlpapi-32/iphlpapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/iphlpapi-32/iphlpapi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/iphlpapi-64/iphlpapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/iphlpapi-64/iphlpapi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/msasn1-32/msasn1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/msasn1-32/msasn1.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/msasn1-64/msasn1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/msasn1-64/msasn1.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/msimg32-32/msimg32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/msimg32-32/msimg32.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/msimg32-64/msimg32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/msimg32-64/msimg32.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/mswsock-32/mswsock.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/mswsock-32/mswsock.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/mswsock-64/mswsock.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/mswsock-64/mswsock.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/opengl32-32/opengl32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/opengl32-32/opengl32.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/opengl32-64/opengl32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/opengl32-64/opengl32.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/profapi-32/profapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/profapi-32/profapi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/profapi-64/profapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/profapi-64/profapi.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/propsys-32/propsys.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/propsys-32/propsys.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/propsys-64/propsys.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/propsys-64/propsys.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/textshaping-32/textshaping.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/textshaping-32/textshaping.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/textshaping-64/textshaping.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/textshaping-64/textshaping.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/version-32/version.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/version-32/version.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/version-64/version.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/version-64/version.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/winhttp-32/winhttp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/winhttp-32/winhttp.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/winhttp-64/winhttp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/winhttp-64/winhttp.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/winmm-32/winmm.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/winmm-32/winmm.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/winmm-64/winmm.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/winmm-64/winmm.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/wldp-32/wldp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/wldp-32/wldp.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/wldp-64/wldp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/wldp-64/wldp.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/xinput9_1_0-32/xinput9_1_0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/xinput9_1_0-32/xinput9_1_0.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/Koaloader/xinput9_1_0-64/xinput9_1_0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/Koaloader/xinput9_1_0-64/xinput9_1_0.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/ScreamAPI/EOSSDK-Win32-Shipping.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/ScreamAPI/EOSSDK-Win32-Shipping.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/ScreamAPI/EOSSDK-Win64-Shipping.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/ScreamAPI/EOSSDK-Win64-Shipping.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/SmokeAPI/steam_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/SmokeAPI/steam_api.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/SmokeAPI/steam_api64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/SmokeAPI/steam_api64.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/UplayR1.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using CreamInstaller.Components; 7 | using CreamInstaller.Forms; 8 | using CreamInstaller.Utility; 9 | using static CreamInstaller.Resources.Resources; 10 | 11 | namespace CreamInstaller.Resources; 12 | 13 | internal static class UplayR1 14 | { 15 | internal static void GetUplayR1Components(this string directory, out string api32, out string api32_o, 16 | out string api64, out string api64_o, 17 | out string config, out string log) 18 | { 19 | api32 = directory + @"\uplay_r1_loader.dll"; 20 | api32_o = directory + @"\uplay_r1_loader_o.dll"; 21 | api64 = directory + @"\uplay_r1_loader64.dll"; 22 | api64_o = directory + @"\uplay_r1_loader64_o.dll"; 23 | config = directory + @"\UplayR1Unlocker.jsonc"; 24 | log = directory + @"\UplayR1Unlocker.log"; 25 | } 26 | 27 | internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) 28 | { 29 | directory.GetUplayR1Components(out _, out _, out _, out _, out string config, out _); 30 | HashSet blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet(); 31 | foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => 32 | extraSelection.DLC.Where(dlc => !dlc.Enabled))) 33 | _ = blacklistDlc.Add(extraDlc); 34 | if (blacklistDlc.Count > 0) 35 | { 36 | /*if (installForm is not null) 37 | installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $"目录 \"{directory}\" . . . ", LogTextBox.Operation);*/ 38 | config.CreateFile(true, installForm)?.Close(); 39 | StreamWriter writer = new(config, true, Encoding.UTF8); 40 | WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), 41 | installForm); 42 | writer.Flush(); 43 | writer.Close(); 44 | } 45 | else if (config.FileExists()) 46 | { 47 | config.DeleteFile(); 48 | installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, 49 | false); 50 | } 51 | } 52 | 53 | private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, 54 | InstallForm installForm = null) 55 | { 56 | writer.WriteLine("{"); 57 | writer.WriteLine(" \"logging\": false,"); 58 | writer.WriteLine(" \"lang\": \"default\","); 59 | writer.WriteLine(" \"hook_loader\": false,"); 60 | if (blacklistDlc.Count > 0) 61 | { 62 | writer.WriteLine(" \"blacklist\": ["); 63 | KeyValuePair lastBlacklistDlc = blacklistDlc.Last(); 64 | foreach (KeyValuePair pair in blacklistDlc) 65 | { 66 | SelectionDLC selectionDlc = pair.Value; 67 | writer.WriteLine($" {selectionDlc.Id}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); 68 | installForm?.UpdateUser( 69 | $"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", 70 | LogTextBox.Action, 71 | false); 72 | } 73 | 74 | writer.WriteLine(" ],"); 75 | } 76 | else 77 | writer.WriteLine(" \"blacklist\": [],"); 78 | 79 | writer.WriteLine("}"); 80 | } 81 | 82 | internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteOthers = true) 83 | => await Task.Run(() => 84 | { 85 | directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, 86 | out string config, out string log); 87 | if (api32_o.FileExists()) 88 | { 89 | if (api32.FileExists()) 90 | { 91 | api32.DeleteFile(true); 92 | installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, 93 | false); 94 | } 95 | 96 | api32_o.MoveFile(api32!); 97 | installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", 98 | LogTextBox.Action, false); 99 | } 100 | 101 | if (api64_o.FileExists()) 102 | { 103 | if (api64.FileExists()) 104 | { 105 | api64.DeleteFile(true); 106 | installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, 107 | false); 108 | } 109 | 110 | api64_o.MoveFile(api64!); 111 | installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", 112 | LogTextBox.Action, false); 113 | } 114 | 115 | if (!deleteOthers) 116 | return; 117 | if (config.FileExists()) 118 | { 119 | config.DeleteFile(); 120 | installForm?.UpdateUser($"删除 配置 {Path.GetFileName(config)}", LogTextBox.Action, false); 121 | } 122 | 123 | if (!log.FileExists()) 124 | return; 125 | log.DeleteFile(); 126 | installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false); 127 | }); 128 | 129 | internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, 130 | bool generateConfig = true) 131 | => await Task.Run(() => 132 | { 133 | directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, 134 | out _, out _); 135 | if (api32.FileExists() && !api32_o.FileExists()) 136 | { 137 | api32.MoveFile(api32_o!, true); 138 | installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", 139 | LogTextBox.Action, false); 140 | } 141 | 142 | if (api32_o.FileExists()) 143 | { 144 | "UplayR1.uplay_r1_loader.dll".WriteManifestResource(api32); 145 | installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, 146 | false); 147 | } 148 | 149 | if (api64.FileExists() && !api64_o.FileExists()) 150 | { 151 | api64.MoveFile(api64_o!, true); 152 | installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", 153 | LogTextBox.Action, false); 154 | } 155 | 156 | if (api64_o.FileExists()) 157 | { 158 | "UplayR1.uplay_r1_loader64.dll".WriteManifestResource(api64); 159 | installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, 160 | false); 161 | } 162 | 163 | if (generateConfig) 164 | CheckConfig(directory, selection, installForm); 165 | }); 166 | 167 | internal static readonly Dictionary> ResourceMD5s = new() 168 | { 169 | [ResourceIdentifier.Uplay32] = 170 | [ 171 | "1977967B2549A38EC2DB39D4C8ED499B" // Uplay R1 Unlocker v2.0.0 172 | ], 173 | [ResourceIdentifier.Uplay64] = 174 | [ 175 | "333FEDD9DC2B299419B37ED1624FF8DB" // Uplay R1 Unlocker v2.0.0 176 | ] 177 | }; 178 | } -------------------------------------------------------------------------------- /CreamInstaller/Resources/UplayR1/uplay_r1_loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/UplayR1/uplay_r1_loader.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/UplayR1/uplay_r1_loader64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/UplayR1/uplay_r1_loader64.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/UplayR2/upc_r2_loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/UplayR2/upc_r2_loader.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/UplayR2/upc_r2_loader64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/UplayR2/upc_r2_loader64.dll -------------------------------------------------------------------------------- /CreamInstaller/Resources/program.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/CreamInstaller/Resources/program.ico -------------------------------------------------------------------------------- /CreamInstaller/Selection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Windows.Forms; 6 | using CreamInstaller.Forms; 7 | using CreamInstaller.Resources; 8 | using CreamInstaller.Utility; 9 | using static CreamInstaller.Resources.Resources; 10 | 11 | namespace CreamInstaller; 12 | 13 | public enum Platform 14 | { 15 | None = 0, 16 | Paradox, 17 | Steam, 18 | Epic, 19 | Ubisoft 20 | } 21 | 22 | internal sealed class Selection : IEquatable 23 | { 24 | internal const string DefaultProxy = "winmm"; 25 | 26 | internal static readonly ConcurrentDictionary All = new(); 27 | 28 | internal readonly HashSet DllDirectories; 29 | internal readonly List<(string directory, BinaryType binaryType)> ExecutableDirectories; 30 | internal readonly HashSet ExtraSelections = []; 31 | internal readonly string Id; 32 | internal readonly string Name; 33 | internal readonly Platform Platform; 34 | internal readonly string RootDirectory; 35 | internal readonly TreeNode TreeNode; 36 | internal string Icon; 37 | internal bool UseProxy; 38 | internal string Proxy; 39 | internal string Product; 40 | internal string Publisher; 41 | internal string SubIcon; 42 | internal string Website; 43 | 44 | internal IEnumerable GetAvailableProxies() 45 | { 46 | if (!Program.UseSmokeAPI && Platform is Platform.Steam or Platform.Paradox) 47 | return CreamAPI.ProxyDLLs; 48 | return EmbeddedResources.Where(r => r.StartsWith("Koaloader", StringComparison.Ordinal)).Select(p => 49 | { 50 | p.GetProxyInfoFromIdentifier(out string proxyName, out _); 51 | return proxyName; 52 | }).ToHashSet(); 53 | } 54 | 55 | private Selection(Platform platform, string id, string name, string rootDirectory, HashSet dllDirectories, 56 | List<(string directory, BinaryType binaryType)> executableDirectories) 57 | { 58 | Platform = platform; 59 | Id = id; 60 | Name = name; 61 | RootDirectory = rootDirectory; 62 | DllDirectories = dllDirectories; 63 | ExecutableDirectories = executableDirectories; 64 | _ = All.TryAdd(this, default); 65 | TreeNode = new() { Tag = Platform, Name = Id, Text = Name }; 66 | SelectForm selectForm = SelectForm.Current; 67 | if (selectForm is null) 68 | return; 69 | Enabled = selectForm.allCheckBox.Checked; 70 | UseProxy = selectForm.proxyAllCheckBox.Checked; 71 | } 72 | 73 | internal static IEnumerable AllEnabled => All.Keys.Where(s => s.Enabled); 74 | 75 | internal bool Enabled 76 | { 77 | get => TreeNode.Checked; 78 | set => TreeNode.Checked = value; 79 | } 80 | 81 | internal IEnumerable DLC => SelectionDLC.All.Keys.Where(dlc => Equals(dlc.Selection, this)); 82 | 83 | public bool Equals(Selection other) => other is not null && 84 | (ReferenceEquals(this, other) || 85 | Id == other.Id && Platform == other.Platform); 86 | 87 | internal static Selection GetOrCreate(Platform platform, string id, string name, string rootDirectory, 88 | HashSet dllDirectories, 89 | List<(string directory, BinaryType binaryType)> executableDirectories) 90 | => FromId(platform, id) ?? 91 | new Selection(platform, id, name, rootDirectory, dllDirectories, executableDirectories); 92 | 93 | internal void Remove() 94 | { 95 | _ = All.TryRemove(this, out _); 96 | TreeNode.Remove(); 97 | foreach (SelectionDLC dlc in DLC) 98 | dlc.Selection = null; 99 | } 100 | 101 | private void Validate(List<(Platform platform, string id, string name)> programsToScan) 102 | { 103 | if (programsToScan is null || !programsToScan.Any(p => p.platform == Platform && p.id == Id)) 104 | { 105 | Remove(); 106 | return; 107 | } 108 | 109 | if (Program.IsGameBlocked(Name, RootDirectory)) 110 | { 111 | Remove(); 112 | return; 113 | } 114 | 115 | if (!RootDirectory.DirectoryExists()) 116 | { 117 | Remove(); 118 | return; 119 | } 120 | 121 | _ = DllDirectories.RemoveWhere(directory => !directory.DirectoryExists()); 122 | if (DllDirectories.Count < 1) 123 | Remove(); 124 | } 125 | 126 | internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan) 127 | { 128 | foreach (Selection selection in All.Keys.ToHashSet()) 129 | selection.Validate(programsToScan); 130 | } 131 | 132 | internal static Selection FromId(Platform platform, string gameId) => 133 | All.Keys.FirstOrDefault(s => s.Platform == platform && s.Id == gameId); 134 | 135 | public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is Selection other && Equals(other); 136 | 137 | public override int GetHashCode() => HashCode.Combine(Id, (int)Platform); 138 | } -------------------------------------------------------------------------------- /CreamInstaller/SelectionDLC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace CreamInstaller; 7 | 8 | public enum DLCType 9 | { 10 | None = 0, 11 | Steam, 12 | SteamHidden, 13 | Epic, 14 | EpicEntitlement 15 | } 16 | 17 | internal sealed class SelectionDLC : IEquatable 18 | { 19 | internal static readonly ConcurrentDictionary All = new(); 20 | 21 | internal readonly string Id; 22 | internal readonly string Name; 23 | internal readonly TreeNode TreeNode; 24 | internal readonly DLCType Type; 25 | internal string Icon; 26 | internal string Product; 27 | internal string Publisher; 28 | private Selection selection; 29 | 30 | private SelectionDLC(DLCType type, string id, string name) 31 | { 32 | Type = type; 33 | Id = id; 34 | Name = name; 35 | TreeNode = new() { Tag = Type, Name = Id, Text = Name }; 36 | } 37 | 38 | internal bool Enabled 39 | { 40 | get => TreeNode.Checked; 41 | set => TreeNode.Checked = value; 42 | } 43 | 44 | internal Selection Selection 45 | { 46 | get => selection; 47 | set 48 | { 49 | if (ReferenceEquals(selection, value)) 50 | return; 51 | selection = value; 52 | if (value is null) 53 | { 54 | _ = All.TryRemove(this, out _); 55 | TreeNode.Remove(); 56 | } 57 | else 58 | { 59 | _ = All.TryAdd(this, default); 60 | _ = value.TreeNode.Nodes.Add(TreeNode); 61 | Enabled = Name != "Unknown" && value.Enabled; 62 | } 63 | } 64 | } 65 | 66 | public bool Equals(SelectionDLC other) 67 | => other is not null && (ReferenceEquals(this, other) || 68 | Type == other.Type && Selection?.Id == other.Selection?.Id && Id == other.Id); 69 | 70 | internal static SelectionDLC GetOrCreate(DLCType type, string gameId, string id, string name) 71 | => FromId(type, gameId, id) ?? new SelectionDLC(type, id, name); 72 | 73 | internal static SelectionDLC FromId(DLCType type, string gameId, string dlcId) 74 | => All.Keys.FirstOrDefault(dlc => dlc.Type == type && dlc.Selection?.Id == gameId && dlc.Id == dlcId); 75 | 76 | public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is SelectionDLC other && Equals(other); 77 | 78 | public override int GetHashCode() => HashCode.Combine((int)Type, Selection?.Id, Id); 79 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/Diagnostics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Microsoft.Win32; 5 | 6 | namespace CreamInstaller.Utility; 7 | 8 | internal static class Diagnostics 9 | { 10 | private static string nppPath; 11 | 12 | private static string NppPath 13 | { 14 | get 15 | { 16 | nppPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Notepad++", "", null) as string; 17 | nppPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Notepad++", "", null) as string; 18 | return nppPath; 19 | } 20 | } 21 | 22 | internal static string GetNotepadPath() 23 | { 24 | string npp = NppPath + @"\notepad++.exe"; 25 | return npp.FileExists() ? npp : Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"; 26 | } 27 | 28 | internal static void OpenFileInNotepad(string path) 29 | { 30 | string npp = NppPath + @"\notepad++.exe"; 31 | if (npp.FileExists()) 32 | OpenFileInNotepadPlusPlus(npp, path); 33 | else 34 | OpenFileInWindowsNotepad(path); 35 | } 36 | 37 | private static void OpenFileInNotepadPlusPlus(string npp, string path) => 38 | Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path }); 39 | 40 | private static void OpenFileInWindowsNotepad(string path) => Process.Start(new ProcessStartInfo 41 | { FileName = "notepad.exe", Arguments = path }); 42 | 43 | internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo 44 | { FileName = "explorer.exe", Arguments = path }); 45 | 46 | internal static void OpenUrlInInternetBrowser(string url) => 47 | Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); 48 | 49 | internal static string ResolvePath(this string path) 50 | { 51 | if (path is null || !path.FileExists() && !path.DirectoryExists()) 52 | return null; 53 | DirectoryInfo info = new(path); 54 | if (info.Parent is null) 55 | return info.Name.ToUpperInvariant(); 56 | string parent = ResolvePath(info.Parent.FullName); 57 | string name = info.Parent.GetFileSystemInfos(info.Name)[0].Name; 58 | return parent is null ? name : Path.Combine(parent, name); 59 | } 60 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/ExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Globalization; 4 | using System.Text; 5 | using System.Windows.Forms; 6 | using CreamInstaller.Forms; 7 | 8 | namespace CreamInstaller.Utility; 9 | 10 | internal static class ExceptionHandler 11 | { 12 | internal static string FormatException(this Exception e) 13 | { 14 | StringBuilder output = new(); 15 | int stackDepth = 0; 16 | while (e is not null) 17 | { 18 | if (stackDepth > 10) 19 | break; 20 | if (output.Length > 0) 21 | _ = output.Append("\n\n"); 22 | string[] stackTrace = e.StackTrace?.Split('\n'); 23 | if (stackTrace is not null && stackTrace.Length > 0) 24 | { 25 | _ = output.Append(CultureInfo.CurrentCulture, $"[{e.HResult & 0x0000FFFF}] {e.GetType()}: {e.Message}"); 26 | foreach (string line in stackTrace) 27 | { 28 | int atNum = line.IndexOf("at ", StringComparison.Ordinal); 29 | int inNum = line.IndexOf("in ", StringComparison.Ordinal); 30 | int ciNum = line.LastIndexOf(@"CreamInstaller\", StringComparison.Ordinal); 31 | int lineNum = line.LastIndexOf(":line ", StringComparison.Ordinal); 32 | if (atNum != -1) 33 | _ = output.Append("\n " + (inNum != -1 ? line[atNum..(inNum - 1)] : line[atNum..]) + 34 | (inNum != -1 35 | ? "\n " + (ciNum != -1 36 | ? "in " + (lineNum != -1 37 | ? line[ciNum..lineNum] + "\n on " + 38 | line[(lineNum + 1)..] 39 | : line[ciNum..]) 40 | : line[inNum..]) 41 | : null)); 42 | } 43 | } 44 | 45 | e = e.InnerException; 46 | stackDepth++; 47 | } 48 | 49 | return output.ToString(); 50 | } 51 | 52 | internal static bool HandleException(this Exception e, Form form = null, string caption = null, 53 | string acceptButtonText = "重试", 54 | string cancelButtonText = "取消") 55 | { 56 | caption ??= Program.Name + " encountered an exception"; 57 | string outputString = e.FormatException(); 58 | if (string.IsNullOrWhiteSpace(outputString)) 59 | outputString = e?.ToString() ?? "Unknown exception"; 60 | using DialogForm dialogForm = new(form ?? Form.ActiveForm); 61 | return dialogForm.Show(SystemIcons.Error, outputString, acceptButtonText, cancelButtonText, caption) is 62 | DialogResult.OK; 63 | } 64 | 65 | internal static void HandleFatalException(this Exception e) 66 | { 67 | e.HandleException(caption: Program.Name + " encountered a fatal exception", acceptButtonText: "OK", 68 | cancelButtonText: null); 69 | Application.Exit(); 70 | } 71 | } 72 | 73 | public class CustomMessageException : Exception 74 | { 75 | public CustomMessageException(string message) : base(message) => Message = message; 76 | 77 | public override string Message { get; } 78 | 79 | public override string ToString() => Message; 80 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/HttpClientManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Drawing; 4 | using System.Globalization; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | #if DEBUG 9 | using CreamInstaller.Forms; 10 | #endif 11 | 12 | namespace CreamInstaller.Utility; 13 | 14 | internal static class HttpClientManager 15 | { 16 | internal static HttpClient HttpClient; 17 | 18 | private static readonly ConcurrentDictionary HttpContentCache = new(); 19 | 20 | internal static void Setup() 21 | { 22 | HttpClient = new(); 23 | HttpClient.DefaultRequestHeaders.UserAgent.Add(new(Program.Name, Program.Version)); 24 | HttpClient.DefaultRequestHeaders.AcceptLanguage.Add(new(CultureInfo.CurrentCulture.ToString())); 25 | } 26 | 27 | internal static async Task EnsureGet(string url) 28 | { 29 | try 30 | { 31 | using HttpRequestMessage request = new(HttpMethod.Get, url); 32 | using HttpResponseMessage response = 33 | await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); 34 | if (response.StatusCode is HttpStatusCode.NotModified && 35 | HttpContentCache.TryGetValue(url, out string content)) 36 | return content; 37 | _ = response.EnsureSuccessStatusCode(); 38 | content = await response.Content.ReadAsStringAsync(); 39 | HttpContentCache[url] = content; 40 | return content; 41 | } 42 | catch (HttpRequestException e) 43 | { 44 | if (e.StatusCode != HttpStatusCode.TooManyRequests) 45 | { 46 | #if DEBUG 47 | DebugForm.Current.Log("Get request failed to " + url + ": " + e, LogTextBox.Warning); 48 | #endif 49 | return null; 50 | } 51 | #if DEBUG 52 | DebugForm.Current.Log("Too many requests to " + url, LogTextBox.Error); 53 | #endif 54 | // do something special? 55 | return null; 56 | } 57 | #if DEBUG 58 | catch (Exception e) 59 | { 60 | DebugForm.Current.Log("Get request failed to " + url + ": " + e, LogTextBox.Warning); 61 | return null; 62 | } 63 | #else 64 | catch 65 | { 66 | return null; 67 | } 68 | #endif 69 | } 70 | 71 | internal static async Task GetImageFromUrl(string url) 72 | { 73 | try 74 | { 75 | return new Bitmap(await HttpClient.GetStreamAsync(new Uri(url))); 76 | } 77 | catch 78 | { 79 | return null; 80 | } 81 | } 82 | 83 | internal static void Dispose() => HttpClient?.Dispose(); 84 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/IconGrabber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace CreamInstaller.Utility; 5 | 6 | internal static class IconGrabber 7 | { 8 | internal const string SteamAppImagesPath = 9 | "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"; 10 | 11 | private const string GoogleFaviconsApiUrl = "https://www.google.com/s2/favicons"; 12 | 13 | internal static Icon ToIcon(this Image image) 14 | { 15 | using Bitmap dialogIconBitmap = new(image, new(image.Width, image.Height)); 16 | return Icon.FromHandle(dialogIconBitmap.GetHicon()); 17 | } 18 | 19 | internal static string GetDomainFaviconUrl(string domain, int size = 16) => 20 | GoogleFaviconsApiUrl + $"?domain={domain}&sz={size}"; 21 | 22 | internal static Image GetFileIconImage(this string path) => 23 | path.FileExists() ? Icon.ExtractAssociatedIcon(path)?.ToBitmap() : null; 24 | 25 | internal static Image GetNotepadImage() => GetFileIconImage(Diagnostics.GetNotepadPath()); 26 | 27 | internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe"); 28 | 29 | internal static Image GetFileExplorerImage() => 30 | GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe"); 31 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/LogTextBox.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Forms; 3 | 4 | namespace CreamInstaller.Utility; 5 | 6 | internal static class LogTextBox 7 | { 8 | internal static readonly Color Background = Color.DarkSlateGray; 9 | internal static readonly Color Operation = Color.LightGray; 10 | internal static readonly Color Action = Color.LightBlue; 11 | internal static readonly Color Success = Color.LightGreen; 12 | internal static readonly Color Cleanup = Color.YellowGreen; 13 | internal static readonly Color Warning = Color.Yellow; 14 | internal static readonly Color Error = Color.DarkOrange; 15 | 16 | internal static void AppendText(this RichTextBox textBox, string text, Color color, bool scroll = false) 17 | { 18 | textBox.SelectionStart = textBox.TextLength; 19 | textBox.SelectionLength = 0; 20 | textBox.SelectionColor = color; 21 | if (scroll) 22 | textBox.ScrollToCaret(); 23 | textBox.AppendText(text); 24 | if (scroll) 25 | textBox.ScrollToCaret(); 26 | textBox.SelectionColor = textBox.ForeColor; 27 | textBox.Invalidate(); 28 | } 29 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/NativeImports.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using static CreamInstaller.Resources.Resources; 3 | 4 | namespace CreamInstaller.Utility; 5 | 6 | internal static partial class NativeImports 7 | { 8 | internal const short SWP_NOACTIVATE = 0x0010; 9 | internal const short SWP_SHOWWINDOW = 0x0040; 10 | internal const short SWP_NOMOVE = 0x0002; 11 | internal const short SWP_NOSIZE = 0x0001; 12 | 13 | internal static readonly nint HWND_NOTOPMOST = new(-2); 14 | internal static readonly nint HWND_TOPMOST = new(-1); 15 | 16 | [LibraryImport("kernel32.dll", SetLastError = true), DefaultDllImportSearchPaths(DllImportSearchPath.System32)] 17 | [return: MarshalAs(UnmanagedType.Bool)] 18 | internal static partial bool GetBinaryType([MarshalAs(UnmanagedType.LPStr)] string lpApplicationName, 19 | out BinaryType lpBinaryType); 20 | 21 | [LibraryImport("user32.dll", SetLastError = true), DefaultDllImportSearchPaths(DllImportSearchPath.System32)] 22 | internal static partial void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy, 23 | uint uFlags); 24 | } -------------------------------------------------------------------------------- /CreamInstaller/Utility/ProgramData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | using Newtonsoft.Json; 8 | 9 | namespace CreamInstaller.Utility; 10 | 11 | internal static class ProgramData 12 | { 13 | private static readonly string DirectoryPathOld = 14 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller"; 15 | 16 | internal static readonly string DirectoryPath = 17 | Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller"; 18 | 19 | internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo"; 20 | private static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt"; 21 | 22 | private static readonly Version MinimumAppInfoVersion = Version.Parse("4.7.0.0"); 23 | 24 | internal static readonly string CooldownPath = DirectoryPath + @"\cooldown"; 25 | 26 | private static readonly string OldProgramChoicesPath = DirectoryPath + @"\choices.txt"; 27 | private static readonly string ProgramChoicesPath = DirectoryPath + @"\choices.json"; 28 | private static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json"; 29 | private static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json"; 30 | 31 | internal static async Task Setup(Form form = null) 32 | => await Task.Run(() => 33 | { 34 | if (DirectoryPathOld.DirectoryExists()) 35 | { 36 | DirectoryPath.DeleteDirectory(); 37 | DirectoryPathOld.MoveDirectory(DirectoryPath, true, form); 38 | } 39 | 40 | DirectoryPath.CreateDirectory(); 41 | if (!AppInfoVersionPath.FileExists() || 42 | !Version.TryParse(AppInfoVersionPath.ReadFile(), out Version version) || 43 | version < MinimumAppInfoVersion) 44 | { 45 | AppInfoPath.DeleteDirectory(); 46 | AppInfoPath.CreateDirectory(); 47 | AppInfoVersionPath.WriteFile(Program.Version); 48 | } 49 | 50 | CooldownPath.CreateDirectory(); 51 | if (OldProgramChoicesPath.FileExists()) 52 | OldProgramChoicesPath.DeleteFile(); 53 | }); 54 | 55 | internal static bool CheckCooldown(string identifier, int cooldown) 56 | { 57 | DateTime now = DateTime.UtcNow; 58 | DateTime lastCheck = GetCooldown(identifier) ?? now; 59 | bool cooldownOver = (now - lastCheck).TotalSeconds > cooldown; 60 | if (cooldownOver || now == lastCheck) 61 | SetCooldown(identifier, now); 62 | return cooldownOver; 63 | } 64 | 65 | private static DateTime? GetCooldown(string identifier) 66 | { 67 | if (!CooldownPath.DirectoryExists()) 68 | return null; 69 | string cooldownFile = CooldownPath + @$"\{identifier}.txt"; 70 | if (!cooldownFile.FileExists()) 71 | return null; 72 | try 73 | { 74 | if (DateTime.TryParse(cooldownFile.ReadFile(), out DateTime cooldown)) 75 | return cooldown; 76 | } 77 | catch 78 | { 79 | // ignored 80 | } 81 | 82 | return null; 83 | } 84 | 85 | private static void SetCooldown(string identifier, DateTime time) 86 | { 87 | CooldownPath.CreateDirectory(); 88 | string cooldownFile = CooldownPath + @$"\{identifier}.txt"; 89 | try 90 | { 91 | cooldownFile.WriteFile(time.ToString(CultureInfo.InvariantCulture)); 92 | } 93 | catch 94 | { 95 | // ignored 96 | } 97 | } 98 | 99 | internal static IEnumerable<(Platform platform, string id)> ReadProgramChoices() 100 | { 101 | if (ProgramChoicesPath.FileExists()) 102 | try 103 | { 104 | if (JsonConvert.DeserializeObject(ProgramChoicesPath.ReadFile(), 105 | typeof(List<(Platform platform, string id)>)) is 106 | List<(Platform platform, string id)> choices) 107 | return choices; 108 | } 109 | catch 110 | { 111 | // ignored 112 | } 113 | 114 | return []; 115 | } 116 | 117 | internal static void WriteProgramChoices(IEnumerable<(Platform platform, string id)> choices) 118 | { 119 | try 120 | { 121 | if (choices is null || !choices.Any()) 122 | ProgramChoicesPath.DeleteFile(); 123 | else 124 | ProgramChoicesPath.WriteFile(JsonConvert.SerializeObject(choices)); 125 | } 126 | catch 127 | { 128 | // ignored 129 | } 130 | } 131 | 132 | internal static IEnumerable<(Platform platform, string gameId, string dlcId)> ReadDlcChoices() 133 | { 134 | if (DlcChoicesPath.FileExists()) 135 | try 136 | { 137 | if (JsonConvert.DeserializeObject(DlcChoicesPath.ReadFile(), 138 | typeof(IEnumerable<(Platform platform, string gameId, string dlcId)>)) is 139 | IEnumerable<(Platform platform, string gameId, string dlcId)> choices) 140 | return choices; 141 | } 142 | catch 143 | { 144 | // ignored 145 | } 146 | 147 | return []; 148 | } 149 | 150 | internal static void WriteDlcChoices(List<(Platform platform, string gameId, string dlcId)> choices) 151 | { 152 | try 153 | { 154 | if (choices is null || choices.Count == 0) 155 | DlcChoicesPath.DeleteFile(); 156 | else 157 | DlcChoicesPath.WriteFile(JsonConvert.SerializeObject(choices)); 158 | } 159 | catch 160 | { 161 | // ignored 162 | } 163 | } 164 | 165 | internal static IEnumerable<(Platform platform, string id, string proxy, bool enabled)> ReadProxyChoices() 166 | { 167 | if (KoaloaderProxyChoicesPath.FileExists()) 168 | try 169 | { 170 | if (JsonConvert.DeserializeObject(KoaloaderProxyChoicesPath.ReadFile(), 171 | typeof(IEnumerable<(Platform platform, string id, string proxy, bool enabled)>)) is 172 | IEnumerable<(Platform platform, string id, string proxy, bool enabled)> choices) 173 | return choices; 174 | } 175 | catch 176 | { 177 | // ignored 178 | } 179 | 180 | return []; 181 | } 182 | 183 | internal static void WriteProxyChoices( 184 | IEnumerable<(Platform platform, string id, string proxy, bool enabled)> choices) 185 | { 186 | try 187 | { 188 | if (choices is null || !choices.Any()) 189 | KoaloaderProxyChoicesPath.DeleteFile(); 190 | else 191 | KoaloaderProxyChoicesPath.WriteFile(JsonConvert.SerializeObject(choices)); 192 | } 193 | catch 194 | { 195 | // ignored 196 | } 197 | } 198 | } -------------------------------------------------------------------------------- /Hashing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !*.ps1 -------------------------------------------------------------------------------- /Hashing/GetHashes.ps1: -------------------------------------------------------------------------------- 1 | $Array32 = [System.Text.StringBuilder]::new().AppendLine('[') 2 | $Array64 = [System.Text.StringBuilder]::new().AppendLine('[') 3 | function Write-Hash([System.IO.FileInfo] $File, [string] $Version) { 4 | $Hash = (Get-FileHash $File -Algorithm MD5).Hash 5 | $Value = "`t`"$Hash`", // CreamAPI $Version" 6 | if ($File.Name.Contains('64')) { 7 | $Array64.AppendLine($Value) | Out-Null 8 | } else { 9 | $Array32.AppendLine($Value) | Out-Null 10 | } 11 | } 12 | Get-ChildItem | ForEach-Object { 13 | if ($_.GetType().Name -eq 'DirectoryInfo') { 14 | $VersionIndex = $_.Name.IndexOf('v') 15 | if ($VersionIndex -eq -1) { Return } 16 | $Release = $_.Name.Substring($VersionIndex).Replace('_', ' ') 17 | Get-ChildItem $_ | ForEach-Object { 18 | if ($_.GetType().Name -eq 'DirectoryInfo') { 19 | $Build = $_.Name -eq 'log_build' ? 'Log build' : 'Non-log build' 20 | Get-ChildItem $_ | ForEach-Object { 21 | if ($_.GetType().Name -eq 'DirectoryInfo') { 22 | Get-ChildItem $_ | ForEach-Object { 23 | if ($_.Extension -eq '.dll') { 24 | Write-Hash $_ "$Release $Build" 25 | } 26 | } 27 | } elseif ($_.Extension -eq '.dll') { 28 | Write-Hash $_ "$Release $Build" 29 | } 30 | } 31 | } 32 | elseif ($_.Extension -eq '.dll') { 33 | Write-Hash $_ "$Release" 34 | } 35 | } 36 | } 37 | } 38 | $Array32.Append(']') | Out-Null 39 | $Array64.Append(']') | Out-Null 40 | Write-Host "32-bit: $($Array32.ToString())" 41 | Write-Host "64-bit: $($Array64.ToString())" 42 | Write-Host 'Press enter to exit . . . ' -NoNewline 43 | $Host.UI.ReadLine() | Out-Null -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 加载缓慢是因为调用了steamdb的api,GPW有时候会墙,这种情况建议挂梯子,或者继续等待。 2 | 3 | CreamInstalle official:https://github.com/pointfeev/CreamInstaller 4 | 5 | 国内用户下载缓慢可用:https://lengkonglovelife.lanzoum.com/b0plmz4sb 密码6666 6 | 7 | 我不是原作者,任何代码问题别来我这边提!谢谢! 8 | 9 | ### CreamInstaller:一个DLC解锁器。 10 | ![QQ20240726-203250](https://github.com/user-attachments/assets/3e82b700-a097-4467-b2a1-43205c62cb44) 11 | ###### **注意:** 这只是预览图; 这不是支持的游戏或配置的列表! 12 | 13 | ##### 该程序使用最新版本的 [Koaloader](https://github.com/acidicoala/Koaloader)、[SmokeAPI](https://github.com/acidicoala/SmokeAPI)、[ScreamAPI]( https://github.com/acidicoala/ScreamAPI)、[Uplay R1 Unlocker](https://github.com/acidicoala/UplayR1Unlocker) 和 [Uplay R2 Unlocker](https://github.com/acidicoala/UplayR2Unlocker) ,由 [acidicoala](https://github.com/acidicoala) 全部嵌入到本软件; 您无需进一步下载关于我们上述已经提到的项目文件! 14 | --- 15 | #### 描述: 16 | 自动查找用户计算机上所有已安装的 Steam、Epic 和 Ubisoft 游戏与DLC,相关DLL 位置, 17 | 18 | 解析SteamCMD、Steam Store和Epic Games Store以获取用户选择的游戏的DLC,提供简单的图形交互菜单 19 | 20 | 利用解析到的信息来维护 DLC 解锁器。 21 | 22 | 该程序的主要功能是**自动检测可解锁的DLC并安装可用于DLC解锁的文件** 23 | 用户选择的游戏和 DLC; 但是,通过使用**右键单击上下文菜单**,用户还可以: 24 | * 自动修补Paradox Launcher 25 | * 在记事本中打开解析的 Steam 或 Epic Games 游戏信息 26 | * 刷新解析的 Steam 或 Epic Games 应用程序信息 27 | * 在资源管理器中打开游戏根目录和重要的DLL目录 28 | * 在默认浏览器中打开SteamDB、ScreamDB、Steam Store、Epic Games Store、Steam Community、Ubisoft Store和官方游戏网站链接(如果适用) 29 | 30 | --- 31 | #### 特征: 32 | * 每当选择 Steam 游戏时,都会根据需要自动下载和安装 SteamCMD。 *用于收集应用程序信息,例如名称、buildid、dlc 列表、仓库等* 33 | * 自动收集和缓存所有选定的 Steam 和 Epic 游戏及其 DLC 的信息。 34 | * Koaloader、SmokeAPI、ScreamAPI、Uplay R1 Unlocker 和 Uplay R2 Unlocker 的自动 DLL 安装和配置生成。 35 | * 自动卸载 Koaloader、CreamAPI、SmokeAPI、ScreamAPI、Uplay R1 Unlocker 和 Uplay R2 Unlocker 的 DLL 和配置。 36 | * 自动修补 Paradox Launcher(并通过右键单击上下文菜单“修复”选项手动修复)。 *当您安装了 CreamAPI、SmokeAPI 或 ScreamAPI 且启动器更新时。* 37 | 38 | --- 39 | #### 安装: 40 | 1. 单击[此处](https://github.com/lengkonglovelife/CreamInstaller-CHS/releases/tag/release)从[GitHub](https://github.com/lengkonglovelife/CreamInstaller-CHS)下载最新版本 )。 41 | 2. 解压缩 没有请搜索Bandzip。 *它是完全独立的。* 42 | 43 | 如果程序无法启动,请尝试下载并安装 [.NET Desktop Runtime 8.0.4](https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0 ) 44 | 然后重新启动电脑。 请注意,该程序目前仅支持 Windows 8+ 64 位计算机,因为 .NET 7+ 不再支持 Windows 7。 45 | 46 | --- 47 | #### 用法: 48 | 1. 启动软件。 *如果未启动,请阅读上面的“安装”部分。* 49 | 2. 选择程序应扫描哪些程序或游戏以查找 DLC。 *该程序自动从 Steam、Epic 和 Ubisoft 目录收集所有已安装的游戏。* 50 | 3. 等待程序下载并安装SteamCMD(如果您选择Steam 游戏)。 *非常快,取决于互联网速度。* 51 | 4. 等待程序收集并缓存所选游戏的信息和 DLC。 *首次运行可能需要很长时间,具体取决于您选择的游戏数量以及它们拥有的 DLC 数量。* 52 | 5. **仔细**选择您想要解锁的游戏 DLC。 *显然没有任何 DLC 解锁器针对每款游戏进行测试!* 53 | 6. 选择是否使用 Koaloader 安装,如果是,则还要选择要使用的代理 DLL。 *如果默认 version.dll 不起作用,请参阅[此处](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) 找到一个能起作用的版本。* 54 | 7. 单击“**生成并安装**”按钮。 55 | 8. 单击**确定**按钮关闭程序。 56 | 9. 如果任何 DLC 解锁器导致您安装的游戏出现问题,只需返回步骤 5 选择**恢复**更改的游戏,然后单击 **卸载** 。 57 | 58 | ##### **注意:** 该程序不会自动为您下载或安装真实的 DLC 文件(正版DLC); 正如程序标题所述,该程序只是一个 *DLC Unlocker* 安装程序。 如果您想要解锁 DLC 的游戏,游戏本体没有安装DLC数据包(大多数游戏都是这种情况),您必须找到、下载这些内容并将其安装到您的游戏中 59 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lengkonglovelife/CreamInstaller_CHS/bca3d7996e64b80291ac504776242a920127fe7c/preview.png --------------------------------------------------------------------------------