From dcbf894932080e786cd7666f42709a150f70bcb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:24:53 +0300 Subject: [PATCH 01/20] Update README.md --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f512104f..f28530b7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,46 @@ ### CreamInstaller: Automatic DLC Unlocker Installer & Configuration Generator -##DONATE +# ⚠️ Disclaimer + +> **This software is an open-source project developed for the community and is not affiliated with any organization or institution.** +> It is shared purely for **educational purposes**, software development testing, and to contribute to the growth of the open-source community. + +--- + +### ⚖️ Legal Responsibility +By using this software, you agree that: +- **All responsibility lies with you, the user.** +- The platform and its contributors provide this software **"as is"**, without any warranty of any kind, express or implied. + This includes, but is not limited to, the warranties of **merchantability**, **fitness for a particular purpose**, or **non-infringement**. + +> ⚠️ **Use it at your own risk.** + +--- + +### 🎯 Intended Use +The primary purpose of this project is to: +- Educate the community by sharing open-source code. +- Facilitate learning and encourage innovation through open collaboration. + +❌ **This software is not intended for production use.** +We strongly recommend purchasing and using professionally licensed software for your needs. + +--- + +### 🙌 Support the Community +If you would like to support the community and this project, consider making a donation: +[![Donate](https://img.shields.io/badge/Donate-Click%20Here-orange?style=for-the-badge&logo=paypal)](https://ubd.one/donate) + +--- + +### 🚨 Report Abuse +If you encounter any abuse or misuse of this software, please report it to: +📧 **[abuse@ubden.com](mailto:abuse@ubden.com)** + +--- + +> Thank you for being a part of the open-source community! 🌟 -# Https://ubd.one/donate ###### The program utilizes the latest versions of [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) and [Uplay R2 Unlocker](https://github.com/acidicoala/UplayR2Unlocker), all by the wonderful [acidicoala](https://github.com/acidicoala), and all downloaded from the posts above and embedded into the program itself; no further downloads necessary on your part! --- @@ -54,3 +92,5 @@ If the program doesn't seem to launch, try downloading and installing [.NET 7 Ru For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]([https://github.com/](https://github.com/ubden/CreamApi/issues)) page! --- + + From b3fdbf119c8f432e16da500919aca85db36a9d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Sun, 13 Apr 2025 15:23:11 +0300 Subject: [PATCH 02/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f28530b7..b99054bd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ### CreamInstaller: Automatic DLC Unlocker Installer & Configuration Generator -# ⚠️ Disclaimer +# ⚠️ Disclaimer (Read before installation !) > **This software is an open-source project developed for the community and is not affiliated with any organization or institution.** > It is shared purely for **educational purposes**, software development testing, and to contribute to the growth of the open-source community. From 0e917eaf18f15d29a5066a449f335da160bf513d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:22:14 +0300 Subject: [PATCH 03/20] Update jekyll-gh-pages.yml --- .github/workflows/jekyll-gh-pages.yml | 32 +++++++++++---------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml index e31d81c5..be5dbe7e 100644 --- a/.github/workflows/jekyll-gh-pages.yml +++ b/.github/workflows/jekyll-gh-pages.yml @@ -1,51 +1,45 @@ -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: Deploy Jekyll with GitHub Pages dependencies preinstalled +name: Deploy Jekyll with GitHub Pages on: - # Runs on pushes targeting the default branch push: branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" cancel-in-progress: false jobs: - # Build job build: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v3 + - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v2 + - name: Build with Jekyll uses: actions/jekyll-build-pages@v1 with: source: ./ destination: ./_site - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - # Deployment job + - name: Upload Artifact + uses: actions/upload-pages-artifact@v1 + deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build + environment: + name: github-pages + url: ${{ steps.deploy.outputs.page_url }} steps: - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + id: deploy + uses: actions/deploy-pages@v1 From a3eaa92af24ab7b89771a5c7c4401736dc34f1c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:25:35 +0300 Subject: [PATCH 04/20] Update jekyll-gh-pages.yml --- .github/workflows/jekyll-gh-pages.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml index be5dbe7e..44c916ce 100644 --- a/.github/workflows/jekyll-gh-pages.yml +++ b/.github/workflows/jekyll-gh-pages.yml @@ -20,17 +20,17 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - + - name: Setup Pages uses: actions/configure-pages@v2 - + - name: Build with Jekyll uses: actions/jekyll-build-pages@v1 with: source: ./ destination: ./_site - - - name: Upload Artifact + + - name: Upload Pages Artifact uses: actions/upload-pages-artifact@v1 deploy: From 2ead8f2dfd1bfd00c2a52d979958537bc4588b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:26:53 +0300 Subject: [PATCH 05/20] Update jekyll-gh-pages.yml --- .github/workflows/jekyll-gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml index 44c916ce..bb93e728 100644 --- a/.github/workflows/jekyll-gh-pages.yml +++ b/.github/workflows/jekyll-gh-pages.yml @@ -32,7 +32,7 @@ jobs: - name: Upload Pages Artifact uses: actions/upload-pages-artifact@v1 - + deploy: runs-on: ubuntu-latest needs: build From 82c2ed9abf94a2dfec33727c039ecf63346e7830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:28:21 +0300 Subject: [PATCH 06/20] Update jekyll-gh-pages.yml --- .github/workflows/jekyll-gh-pages.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml index bb93e728..24e6dbc8 100644 --- a/.github/workflows/jekyll-gh-pages.yml +++ b/.github/workflows/jekyll-gh-pages.yml @@ -30,9 +30,6 @@ jobs: source: ./ destination: ./_site - - name: Upload Pages Artifact - uses: actions/upload-pages-artifact@v1 - deploy: runs-on: ubuntu-latest needs: build From 105b4ed84ce98eebfcce1ad71bf67d79010c0a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:28:37 +0300 Subject: [PATCH 07/20] Delete .github/workflows/delete_releases.yml --- .github/workflows/delete_releases.yml | 30 --------------------------- 1 file changed, 30 deletions(-) delete mode 100644 .github/workflows/delete_releases.yml diff --git a/.github/workflows/delete_releases.yml b/.github/workflows/delete_releases.yml deleted file mode 100644 index 456302b5..00000000 --- a/.github/workflows/delete_releases.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Delete Releases - -on: - workflow_dispatch: - inputs: - tagPattern: - description: 'Tag pattern' - required: true - default: 'v1' - -jobs: - delete: - name: Delete - runs-on: windows-latest - if: ${{ github.event.repository.owner.id }} == ${{ github.event.sender.id }} - - permissions: write-all - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Delete - uses: dev-drprasad/delete-older-releases@v0.2.1 - with: - keep_latest: 0 - delete_tags: true - delete_tag_pattern: ${{ github.event.inputs.tagPattern }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From bacaa30968789979ad701564a4674c11e9d8d1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:28:45 +0300 Subject: [PATCH 08/20] Delete .github/workflows/test.yml --- .github/workflows/test.yml | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index cbb0f433..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - test: - name: Test - runs-on: windows-latest - permissions: - actions: read - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.x.x - - - name: Restore - run: dotnet restore - - - name: Build - run: dotnet build --no-restore --configuration Release /p:UseSharedCompilation=false - - - name: Test - run: dotnet test --no-build --verbosity normal --configuration Release From 12e5b6989ae4f786eb615dbc18830b1bb19a5435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:29:34 +0300 Subject: [PATCH 09/20] Delete .github/workflows/jekyll-gh-pages.yml --- .github/workflows/jekyll-gh-pages.yml | 42 --------------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/jekyll-gh-pages.yml diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml deleted file mode 100644 index 24e6dbc8..00000000 --- a/.github/workflows/jekyll-gh-pages.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Deploy Jekyll with GitHub Pages - -on: - push: - branches: ["main"] - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Pages - uses: actions/configure-pages@v2 - - - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1 - with: - source: ./ - destination: ./_site - - deploy: - runs-on: ubuntu-latest - needs: build - environment: - name: github-pages - url: ${{ steps.deploy.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deploy - uses: actions/deploy-pages@v1 From 08b42c5b937f322af7fb52b4e3086dadf91fa801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:02:27 +0300 Subject: [PATCH 10/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b99054bd..e993a882 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ### CreamInstaller: Automatic DLC Unlocker Installer & Configuration Generator -# ⚠️ Disclaimer (Read before installation !) +# ⚠️ Disclaimer (Read before installation and Follow Us on Github !) > **This software is an open-source project developed for the community and is not affiliated with any organization or institution.** > It is shared purely for **educational purposes**, software development testing, and to contribute to the growth of the open-source community. From 9aff18aac579a73e443b0e7d959d7ee67da39d42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:36:45 +0000 Subject: [PATCH 11/20] Initial plan From 185b1e7ae07d6b9a1a3ccf07ae95b620ba5dc875 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:38:11 +0000 Subject: [PATCH 12/20] Initial plan From 4f7f87cbf0405548c25e0ff7bfedb71b167d257c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:38:26 +0000 Subject: [PATCH 13/20] Initial plan From 3539775ad0b35c9fcc72ed7b51f0825174ace4a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:40:32 +0000 Subject: [PATCH 14/20] Add global.json to fix SDK resolution and update README Co-authored-by: ubden <44896964+ubden@users.noreply.github.com> --- README.md | 8 ++++++++ global.json | 7 +++++++ 2 files changed, 15 insertions(+) create mode 100644 global.json diff --git a/README.md b/README.md index e993a882..f85ce6ec 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,14 @@ games and DLCs the user selects; however, through the use of **right-click conte If the program doesn't seem to launch, try downloading and installing [.NET 7 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-7.0.2-windows-x64-installer). +--- +#### Building from Source: +To build the project from source code, you need: +1. [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) or later +2. Visual Studio 2022 (or later) with .NET desktop development workload, or Visual Studio Code with C# extension + +**Note:** While the application targets .NET 7, you can build it with .NET 8 SDK or later. The `global.json` file ensures SDK compatibility. + --- #### **NOTE:** This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many do not), you have to find, download, and install those yourself. Preferably, you should be referring to the proper cs.rin.ru post for the game(s) you're tinkering with; you'll usually find any answer to your problems there. diff --git a/global.json b/global.json new file mode 100644 index 00000000..f7fb55b4 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.100", + "rollForward": "latestMinor", + "allowPrerelease": false + } +} From 8bd3f97be1ea88c40feb16b8acaf06e166dbbcff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:41:33 +0000 Subject: [PATCH 15/20] Fix: Change extra_inventory_items from object to array in SmokeAPI config Co-authored-by: ubden <44896964+ubden@users.noreply.github.com> --- CreamInstaller/Resources/SmokeAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CreamInstaller/Resources/SmokeAPI.cs b/CreamInstaller/Resources/SmokeAPI.cs index 6462e3ec..86133da3 100644 --- a/CreamInstaller/Resources/SmokeAPI.cs +++ b/CreamInstaller/Resources/SmokeAPI.cs @@ -102,7 +102,7 @@ private static void WriteConfig(StreamWriter writer, string appId, else writer.WriteLine(" \"override_dlc_status\": {},"); writer.WriteLine(" \"auto_inject_inventory\": true,"); - writer.WriteLine(" \"extra_inventory_items\": {},"); + writer.WriteLine(" \"extra_inventory_items\": [],"); if (injectDlc.Count > 0 || extraApps.Count > 0) { writer.WriteLine(" \"extra_dlcs\": {"); From f7de4cb25b7606f75fccc3412f41ad4e03235bf4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:22:03 +0000 Subject: [PATCH 16/20] Add antivirus/DLL false positive warning section to README Agent-Logs-Url: https://github.com/ubden/CreamApi-CreamInstaller/sessions/dcd61999-e89a-498d-9245-db51edac20e0 Co-authored-by: ubden <44896964+ubden@users.noreply.github.com> --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index f85ce6ec..736e51ae 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,21 @@ --- +### 🛡️ Antivirus / False Positive Warning + +> ⚠️ **As is widely known, all software that modifies or interacts with DLL files may be flagged as a virus by antivirus programs.** + +VirusTotal scan results and antivirus software **may detect this project as malicious**. However: + +- The **entire project is open source** — no encrypted or obfuscated code is included. +- It is intended solely for **educational and development purposes**. +- This software is **for experienced users only**. If you are not comfortable reviewing source code yourself, **downloading and using this software is not recommended**. + +For further reading on antivirus false positives related to DLL-interacting tools, see: +📄 [Springer – *International Journal of Information Security* (2024)](https://link.springer.com/article/10.1007/s10207-024-00836-w) + +--- + ### ⚖️ Legal Responsibility By using this software, you agree that: - **All responsibility lies with you, the user.** From 8da0f3a9356fb9c15d39acc903d130802c9e3745 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:26:28 +0000 Subject: [PATCH 17/20] Add Wikipedia antivirus link to README warning section Agent-Logs-Url: https://github.com/ubden/CreamApi-CreamInstaller/sessions/4330adfa-3a87-42b3-9d87-6b672344e50a Co-authored-by: ubden <44896964+ubden@users.noreply.github.com> --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 736e51ae..60523c9e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ VirusTotal scan results and antivirus software **may detect this project as mali - This software is **for experienced users only**. If you are not comfortable reviewing source code yourself, **downloading and using this software is not recommended**. For further reading on antivirus false positives related to DLL-interacting tools, see: -📄 [Springer – *International Journal of Information Security* (2024)](https://link.springer.com/article/10.1007/s10207-024-00836-w) +📄 [Springer – *International Journal of Information Security* (2024)](https://link.springer.com/article/10.1007/s10207-024-00836-w) +📖 [Wikipedia – Antivirus Software](https://en.wikipedia.org/wiki/Antivirus_software) --- From cd0399232aea2216e07bea10cf982f4556ded05b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:52:18 +0000 Subject: [PATCH 18/20] fix: bugs, performance, security improvements and AV false-positive comments Agent-Logs-Url: https://github.com/ubden/CreamApi-CreamInstaller/sessions/8e5d9ac7-4144-49da-ac9b-411226fbdc45 Co-authored-by: ubden <44896964+ubden@users.noreply.github.com> --- CreamInstaller/Components/CustomForm.cs | 8 ++++ CreamInstaller/Platforms/Steam/SteamCMD.cs | 14 ++++++ CreamInstaller/Properties/Resources.resx | 2 +- CreamInstaller/Resources/Koaloader.cs | 18 ++++++-- CreamInstaller/Resources/Resources.cs | 25 ++++++++++- CreamInstaller/Resources/ScreamAPI.cs | 10 +++-- CreamInstaller/Resources/SmokeAPI.cs | 13 ++++-- CreamInstaller/Resources/UplayR1.cs | 9 ++-- CreamInstaller/Resources/UplayR2.cs | 9 ++-- CreamInstaller/Utility/Diagnostics.cs | 47 +++++++++++++++++---- CreamInstaller/Utility/HttpClientManager.cs | 14 +++++- 11 files changed, 143 insertions(+), 26 deletions(-) diff --git a/CreamInstaller/Components/CustomForm.cs b/CreamInstaller/Components/CustomForm.cs index 2dedb2e1..936947bd 100644 --- a/CreamInstaller/Components/CustomForm.cs +++ b/CreamInstaller/Components/CustomForm.cs @@ -86,6 +86,10 @@ private void OnHelpButtonClicked(object sender, EventArgs args) private void OnActivation(object sender, EventArgs args) => Activate(); + // ANTIVIRUS FALSE POSITIVE WARNING: + // SetWindowPos is a Win32 API imported from user32.dll. It is used here only to bring the + // application's own window to the front without stealing focus (SWP_NOACTIVATE). This is a + // standard WinForms UI technique and is not indicative of screen-scraping or UAC bypass. [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] private static extern void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); @@ -107,6 +111,10 @@ internal void InheritLocation(Form fromForm) Location = new(X, Y); } + // ANTIVIRUS FALSE POSITIVE WARNING: + // OnKeyPress captures Shift+S and copies the window contents to the clipboard as a bitmap. + // This is a built-in debug screenshot helper for reporting UI bugs. It reads only from + // this application's own window area via Graphics.CopyFromScreen and is not a keylogger. private void OnKeyPress(object s, KeyPressEventArgs e) { if (e.KeyChar != 'S') diff --git a/CreamInstaller/Platforms/Steam/SteamCMD.cs b/CreamInstaller/Platforms/Steam/SteamCMD.cs index ece31b98..40b0c31c 100644 --- a/CreamInstaller/Platforms/Steam/SteamCMD.cs +++ b/CreamInstaller/Platforms/Steam/SteamCMD.cs @@ -62,6 +62,10 @@ private static async Task Run(string appId) } if (Program.Canceled) return ""; + // ANTIVIRUS FALSE POSITIVE WARNING: + // Launches steamcmd.exe with +login anonymous +app_info_print +quit to + // retrieve game metadata from Steam. The process output is captured and parsed. + // SteamCMD is an official Valve tool; launching it does not indicate malicious intent. ProcessStartInfo processStartInfo = new() { FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, @@ -117,6 +121,12 @@ private static async Task Run(string appId) goto wait_for_lock; }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Setup downloads steamcmd.zip from Valve's official CDN (steamcdn-a.akamaihd.net), + // extracts the ZIP archive, and runs steamcmd.exe once with +quit to initialise it. + // Downloading and extracting an executable is flagged by some AV heuristics as a dropper; + // the source is Valve's own content delivery network and is used only when steamcmd.exe + // is not already present in the CreamInstaller data directory. internal static async Task Setup(IProgress progress) { await Cleanup(); @@ -271,6 +281,10 @@ internal static async Task> ParseDlcAppIds(VProperty appInfo) return dlcIds; }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Kill enumerates running processes by name ("steamcmd") and terminates them. + // Process-enumeration and process-kill APIs are used here only to clean up child + // steamcmd.exe instances that were started by this application. private static async Task Kill() { List tasks = Process.GetProcessesByName("steamcmd").Select(process => Task.Run(() => diff --git a/CreamInstaller/Properties/Resources.resx b/CreamInstaller/Properties/Resources.resx index 56c0201b..cee2dae6 100644 --- a/CreamInstaller/Properties/Resources.resx +++ b/CreamInstaller/Properties/Resources.resx @@ -119,6 +119,6 @@ - ..\resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/CreamInstaller/Resources/Koaloader.cs b/CreamInstaller/Resources/Koaloader.cs index c4fc6a63..17c17375 100644 --- a/CreamInstaller/Resources/Koaloader.cs +++ b/CreamInstaller/Resources/Koaloader.cs @@ -35,6 +35,12 @@ internal static void GetKoaloaderComponents(this string directory, out string ol config = directory + @"\Koaloader.config.json"; } + // ANTIVIRUS FALSE POSITIVE WARNING: + // WriteProxy writes a Koaloader DLL (a DLL proxy / shim) named after a well-known Windows + // system DLL (e.g. version.dll, d3d9.dll) into the game's installation directory. + // This is a standard DLL-proxying technique used by the Koaloader project to load + // additional modules. The DLL is extracted from embedded resources and validated via MD5. + // This pattern is commonly misidentified as DLL-hijacking malware by antivirus engines. private static void WriteProxy(this string path, string proxyName, BinaryType binaryType) { foreach (string resourceIdentifier in EmbeddedResources.FindAll(r => r.StartsWith("Koaloader"))) @@ -74,15 +80,15 @@ private static void CheckConfig(string directory, InstallForm installForm = null } SortedList targets = new(PlatformIdComparer.String); SortedList modules = new(PlatformIdComparer.String); + // NOTE: targets and modules are intentionally left empty here; the block below is + // preserved for future use when per-game target/module configuration is implemented. if (targets.Any() || modules.Any()) { /*if (installForm is not null) installForm.UpdateUser("Generating Koaloader configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ File.Create(config).Close(); - StreamWriter writer = new(config, true, Encoding.UTF8); + using StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, targets, modules, installForm); - writer.Flush(); - writer.Close(); } else if (File.Exists(config)) { @@ -165,6 +171,12 @@ internal static async Task Uninstall(string directory, string rootDirectory = nu await Uninstall(rootDirectory, null, installForm, deleteConfig); }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // The Install method writes Koaloader proxy DLLs and the appropriate unlocker DLLs + // (SmokeAPI32/64.dll, ScreamAPI32/64.dll, UplayR1/R2Unlocker32/64.dll) into the game + // directory. It also deletes outdated proxy DLLs from previous installations. + // Writing named DLLs to application directories is the intended installation procedure — + // not malware dropper behavior. All written binaries are MD5-verified. internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection, string rootDirectory = null, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => diff --git a/CreamInstaller/Resources/Resources.cs b/CreamInstaller/Resources/Resources.cs index 1d625284..13bcdc07 100644 --- a/CreamInstaller/Resources/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,6 +16,11 @@ internal static class Resources { private static List embeddedResources; + // Performance: cache computed MD5 hashes keyed by file path to avoid re-hashing the same + // file on every call to IsResourceFile(). The cache is invalidated only when the file is + // written (Write() clears the entry) so stale hashes are never returned. + private static readonly ConcurrentDictionary Md5Cache = new(StringComparer.OrdinalIgnoreCase); + private static readonly Dictionary> ResourceMD5s = new() { { @@ -432,8 +438,14 @@ internal static List EmbeddedResources } } + // ANTIVIRUS FALSE POSITIVE WARNING: + // The following two Write methods extract DLL binaries that are embedded in this assembly's + // resources and write them to the target game directory. This is the core installation step + // of the DLC unlockers (SmokeAPI, ScreamAPI, Koaloader, UplayR1/R2 Unlocker). + // These files are verified against a hardcoded MD5 whitelist before and after writing. internal static void Write(this string resourceIdentifier, string filePath) { + Md5Cache.TryRemove(filePath, out _); // invalidate cached hash for overwritten file using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); resource?.CopyTo(file); @@ -441,6 +453,7 @@ internal static void Write(this string resourceIdentifier, string filePath) internal static void Write(this byte[] resource, string filePath) { + Md5Cache.TryRemove(filePath, out _); // invalidate cached hash for overwritten file using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write); fileStream.Write(resource); } @@ -518,6 +531,11 @@ private static bool IsCommonIncorrectExecutable(this string rootDirectory, strin || subPath.Contains("ANTICHEAT"); } + // ANTIVIRUS FALSE POSITIVE WARNING: + // Scans the game's installation directory tree looking for platform-specific DLL files + // (steam_api.dll, EOSSDK-*.dll, uplay_r1_loader*.dll, upc_r2_loader*.dll) to determine + // which subdirectories contain unlocker targets. This recursive directory + file scan + // is purely read-only detection, not malicious file enumeration. internal static async Task> GetDllDirectoriesFromGameDirectory(this string gameDirectory, Platform platform) => await Task.Run(() => { @@ -582,12 +600,17 @@ private static string ComputeMD5(this string filePath) { if (!File.Exists(filePath)) return null; + // Performance: return cached hash if the file has already been hashed this session. + if (Md5Cache.TryGetValue(filePath, out string cached)) + return cached; #pragma warning disable CA5351 using MD5 md5 = MD5.Create(); #pragma warning restore CA5351 using FileStream stream = File.OpenRead(filePath); byte[] hash = md5.ComputeHash(stream); - return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant(); + string result = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant(); + Md5Cache[filePath] = result; + return result; } internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier) diff --git a/CreamInstaller/Resources/ScreamAPI.cs b/CreamInstaller/Resources/ScreamAPI.cs index fbc039f3..5f7e311c 100644 --- a/CreamInstaller/Resources/ScreamAPI.cs +++ b/CreamInstaller/Resources/ScreamAPI.cs @@ -40,11 +40,9 @@ internal static void CheckConfig(string directory, ProgramSelection selection, I /*if (installForm is not null) installForm.UpdateUser("Generating ScreamAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ File.Create(config).Close(); - StreamWriter writer = new(config, true, Encoding.UTF8); + using StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, new(overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), new(entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); - writer.Flush(); - writer.Close(); } else if (File.Exists(config)) { @@ -101,6 +99,9 @@ private static void WriteConfig(StreamWriter writer, SortedList await Task.Run(() => { @@ -139,6 +140,9 @@ internal static async Task Uninstall(string directory, InstallForm installForm = } }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Install renames EOSSDK-Win32/64-Shipping.dll to *_o backups and writes ScreamAPI in + // their place. Replacing Epic Online Services SDK DLLs is the intended installation method. internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { diff --git a/CreamInstaller/Resources/SmokeAPI.cs b/CreamInstaller/Resources/SmokeAPI.cs index 86133da3..9fd27d0c 100644 --- a/CreamInstaller/Resources/SmokeAPI.cs +++ b/CreamInstaller/Resources/SmokeAPI.cs @@ -61,12 +61,10 @@ internal static void CheckConfig(string directory, ProgramSelection selection, I /*if (installForm is not null) installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ File.Create(config).Close(); - StreamWriter writer = new(config, true, Encoding.UTF8); + using StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, selection.Id, new(extraApps.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); - writer.Flush(); - writer.Close(); } else if (File.Exists(config)) { @@ -151,6 +149,10 @@ private static void WriteConfig(StreamWriter writer, string appId, writer.WriteLine("}"); } + // ANTIVIRUS FALSE POSITIVE WARNING: + // Uninstall deletes the SmokeAPI DLL and moves the original steam_api.dll / steam_api64.dll + // backups back to their original names. Renaming and deleting DLLs in Steam game directories + // may be flagged as malicious file manipulation; this is the intended restore procedure. internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteOthers = true) => await Task.Run(() => { @@ -211,6 +213,11 @@ internal static async Task Uninstall(string directory, InstallForm installForm = } }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Install renames the original steam_api.dll / steam_api64.dll to *_o.dll backups and + // writes the SmokeAPI replacement DLL in their place. Replacing Steamworks DLLs is the + // intended SmokeAPI installation method. This pattern is commonly flagged as a trojan + // dropper or DLL hijack by heuristic antivirus scanners. internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { diff --git a/CreamInstaller/Resources/UplayR1.cs b/CreamInstaller/Resources/UplayR1.cs index c0fce841..bea89a58 100644 --- a/CreamInstaller/Resources/UplayR1.cs +++ b/CreamInstaller/Resources/UplayR1.cs @@ -34,10 +34,8 @@ internal static void CheckConfig(string directory, ProgramSelection selection, I /*if (installForm is not null) installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ File.Create(config).Close(); - StreamWriter writer = new(config, true, Encoding.UTF8); + using StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); - writer.Flush(); - writer.Close(); } else if (File.Exists(config)) { @@ -71,6 +69,8 @@ private static void WriteConfig(StreamWriter writer, SortedList await Task.Run(() => { @@ -109,6 +109,9 @@ internal static async Task Uninstall(string directory, InstallForm installForm = } }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Install renames uplay_r1_loader*.dll to *_o backups and writes the Uplay R1 Unlocker DLL. + // Replacing Uplay/Ubisoft Connect loader DLLs is the intended installation method. internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { diff --git a/CreamInstaller/Resources/UplayR2.cs b/CreamInstaller/Resources/UplayR2.cs index 1f2c8ced..8e21e5a7 100644 --- a/CreamInstaller/Resources/UplayR2.cs +++ b/CreamInstaller/Resources/UplayR2.cs @@ -36,10 +36,8 @@ internal static void CheckConfig(string directory, ProgramSelection selection, I /*if (installForm is not null) installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ File.Create(config).Close(); - StreamWriter writer = new(config, true, Encoding.UTF8); + using StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); - writer.Flush(); - writer.Close(); } else if (File.Exists(config)) { @@ -75,6 +73,8 @@ private static void WriteConfig(StreamWriter writer, SortedList await Task.Run(() => { @@ -116,6 +116,9 @@ internal static async Task Uninstall(string directory, InstallForm installForm = } }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Install renames upc_r2_loader*.dll to *_o backups and writes the Uplay R2 Unlocker DLL. + // Replacing Ubisoft Connect R2 loader DLLs is the intended installation method. internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { diff --git a/CreamInstaller/Utility/Diagnostics.cs b/CreamInstaller/Utility/Diagnostics.cs index d44317a1..b3f07b6f 100644 --- a/CreamInstaller/Utility/Diagnostics.cs +++ b/CreamInstaller/Utility/Diagnostics.cs @@ -9,6 +9,9 @@ internal static class Diagnostics { private static string notepadPlusPlusPath; + // ANTIVIRUS FALSE POSITIVE WARNING: + // Reading registry keys from HKEY_LOCAL_MACHINE to detect installed applications. + // This is standard application-detection behavior, not malicious registry enumeration. internal static string NotepadPlusPlusPath { get @@ -21,26 +24,54 @@ internal static string NotepadPlusPlusPath internal static string GetNotepadPath() { - string npp = NotepadPlusPlusPath + @"\notepad++.exe"; - return File.Exists(npp) ? npp : Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"; + // Bug fix: NotepadPlusPlusPath may be null; guard before string concatenation. + string nppBase = NotepadPlusPlusPath; + string npp = nppBase is not null ? nppBase + @"\notepad++.exe" : null; + return npp is not null && File.Exists(npp) + ? npp + : Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"; } internal static void OpenFileInNotepad(string path) { - string npp = NotepadPlusPlusPath + @"\notepad++.exe"; - if (File.Exists(npp)) + // Bug fix: NotepadPlusPlusPath may be null; guard before string concatenation. + string nppBase = NotepadPlusPlusPath; + string npp = nppBase is not null ? nppBase + @"\notepad++.exe" : null; + if (npp is not null && File.Exists(npp)) OpenFileInNotepadPlusPlus(npp, path); else OpenFileInWindowsNotepad(path); } - private static void OpenFileInNotepadPlusPlus(string npp, string path) => Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // The following three methods launch external processes (Notepad++, notepad.exe, explorer.exe). + // They are invoked only on explicit user request to view a configuration file or directory. + // ArgumentList is used instead of the Arguments string to prevent argument-injection vulnerabilities. + private static void OpenFileInNotepadPlusPlus(string npp, string path) + { + ProcessStartInfo psi = new() { FileName = npp, UseShellExecute = false }; + psi.ArgumentList.Add(path); + _ = Process.Start(psi); + } - private static void OpenFileInWindowsNotepad(string path) => Process.Start(new ProcessStartInfo { FileName = "notepad.exe", Arguments = path }); + private static void OpenFileInWindowsNotepad(string path) + { + ProcessStartInfo psi = new() { FileName = "notepad.exe", UseShellExecute = false }; + psi.ArgumentList.Add(path); + _ = Process.Start(psi); + } - internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo { FileName = "explorer.exe", Arguments = path }); + internal static void OpenDirectoryInFileExplorer(string path) + { + ProcessStartInfo psi = new() { FileName = "explorer.exe", UseShellExecute = false }; + psi.ArgumentList.Add(path); + _ = Process.Start(psi); + } - internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Opens a URL in the user's default browser via ShellExecute. This is standard behavior + // for linking to the project's GitHub page from the Help button — not drive-by download. + internal static void OpenUrlInInternetBrowser(string url) => _ = Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); internal static string BeautifyPath(this string path) => path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path)); } \ No newline at end of file diff --git a/CreamInstaller/Utility/HttpClientManager.cs b/CreamInstaller/Utility/HttpClientManager.cs index 2fddcad6..26fc1556 100644 --- a/CreamInstaller/Utility/HttpClientManager.cs +++ b/CreamInstaller/Utility/HttpClientManager.cs @@ -12,10 +12,19 @@ internal static class HttpClientManager internal static void Setup() { - HttpClient = new(); + HttpClient = new() + { + // Performance: limit request duration so the UI does not hang indefinitely + // on slow or unresponsive endpoints (Steam API, Epic API, image CDNs). + Timeout = TimeSpan.FromSeconds(60) + }; HttpClient.DefaultRequestHeaders.Add("User-Agent", $"CI{Program.Version.Replace(".", "")}"); } + // ANTIVIRUS FALSE POSITIVE WARNING: + // This method makes outbound HTTPS GET requests to Steam Store / Epic Games Store APIs + // to retrieve game and DLC metadata. All requests are read-only and user-initiated. + // No data is sent to any third-party server beyond the query string. internal static async Task EnsureGet(string url) { try @@ -43,6 +52,9 @@ internal static async Task GetDocumentNodes(string url, stri internal static HtmlNodeCollection GetDocumentNodes(this HtmlDocument htmlDocument, string xpath) => htmlDocument.DocumentNode?.SelectNodes(xpath); + // ANTIVIRUS FALSE POSITIVE WARNING: + // Downloads game cover art images from Steam / Epic CDNs for display in the UI. + // This is a standard image-fetch pattern, not a silent payload download. internal static async Task GetImageFromUrl(string url) { try From f1d19911beddd6245a6a96d1d3206f14df086531 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:56:09 +0000 Subject: [PATCH 19/20] fix: address code review - path validation, cache naming, race condition fix Agent-Logs-Url: https://github.com/ubden/CreamApi-CreamInstaller/sessions/8e5d9ac7-4144-49da-ac9b-411226fbdc45 Co-authored-by: ubden <44896964+ubden@users.noreply.github.com> --- CreamInstaller/Resources/Resources.cs | 12 ++++++------ CreamInstaller/Utility/Diagnostics.cs | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CreamInstaller/Resources/Resources.cs b/CreamInstaller/Resources/Resources.cs index 13bcdc07..481a3b79 100644 --- a/CreamInstaller/Resources/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -18,8 +18,8 @@ internal static class Resources // Performance: cache computed MD5 hashes keyed by file path to avoid re-hashing the same // file on every call to IsResourceFile(). The cache is invalidated only when the file is - // written (Write() clears the entry) so stale hashes are never returned. - private static readonly ConcurrentDictionary Md5Cache = new(StringComparer.OrdinalIgnoreCase); + // written (Write() clears the entry after writing) so stale hashes are never returned. + private static readonly ConcurrentDictionary md5Cache = new(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> ResourceMD5s = new() { @@ -445,17 +445,17 @@ internal static List EmbeddedResources // These files are verified against a hardcoded MD5 whitelist before and after writing. internal static void Write(this string resourceIdentifier, string filePath) { - Md5Cache.TryRemove(filePath, out _); // invalidate cached hash for overwritten file using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); resource?.CopyTo(file); + md5Cache.TryRemove(filePath, out _); // invalidate cached hash after file is written } internal static void Write(this byte[] resource, string filePath) { - Md5Cache.TryRemove(filePath, out _); // invalidate cached hash for overwritten file using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write); fileStream.Write(resource); + md5Cache.TryRemove(filePath, out _); // invalidate cached hash after file is written } internal static bool IsFilePathLocked(this string filePath) @@ -601,7 +601,7 @@ private static string ComputeMD5(this string filePath) if (!File.Exists(filePath)) return null; // Performance: return cached hash if the file has already been hashed this session. - if (Md5Cache.TryGetValue(filePath, out string cached)) + if (md5Cache.TryGetValue(filePath, out string cached)) return cached; #pragma warning disable CA5351 using MD5 md5 = MD5.Create(); @@ -609,7 +609,7 @@ private static string ComputeMD5(this string filePath) using FileStream stream = File.OpenRead(filePath); byte[] hash = md5.ComputeHash(stream); string result = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant(); - Md5Cache[filePath] = result; + md5Cache[filePath] = result; return result; } diff --git a/CreamInstaller/Utility/Diagnostics.cs b/CreamInstaller/Utility/Diagnostics.cs index b3f07b6f..ad48eba1 100644 --- a/CreamInstaller/Utility/Diagnostics.cs +++ b/CreamInstaller/Utility/Diagnostics.cs @@ -49,6 +49,8 @@ internal static void OpenFileInNotepad(string path) // ArgumentList is used instead of the Arguments string to prevent argument-injection vulnerabilities. private static void OpenFileInNotepadPlusPlus(string npp, string path) { + if (!File.Exists(path)) + return; ProcessStartInfo psi = new() { FileName = npp, UseShellExecute = false }; psi.ArgumentList.Add(path); _ = Process.Start(psi); @@ -56,6 +58,8 @@ private static void OpenFileInNotepadPlusPlus(string npp, string path) private static void OpenFileInWindowsNotepad(string path) { + if (!File.Exists(path)) + return; ProcessStartInfo psi = new() { FileName = "notepad.exe", UseShellExecute = false }; psi.ArgumentList.Add(path); _ = Process.Start(psi); @@ -63,6 +67,8 @@ private static void OpenFileInWindowsNotepad(string path) internal static void OpenDirectoryInFileExplorer(string path) { + if (!Directory.Exists(path)) + return; ProcessStartInfo psi = new() { FileName = "explorer.exe", UseShellExecute = false }; psi.ArgumentList.Add(path); _ = Process.Start(psi); From 2f9ec3389087a028c125af226700dc1329ac3342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ubden=C2=AE=F0=9F=9B=A1=EF=B8=8F?= <44896964+ubden@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:00:29 +0300 Subject: [PATCH 20/20] Update README disclaimer formatting and structure --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 60523c9e..99766c9e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ### CreamInstaller: Automatic DLC Unlocker Installer & Configuration Generator -# ⚠️ Disclaimer (Read before installation and Follow Us on Github !) +# ⚠️ Disclaimer +## (Read before installation and Follow Us on Github !) > **This software is an open-source project developed for the community and is not affiliated with any organization or institution.** > It is shared purely for **educational purposes**, software development testing, and to contribute to the growth of the open-source community.