diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 380f0fc59..014edd09c 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -6,6 +6,9 @@ on:
pull_request:
branches: [ "v6", v* ]
+env:
+ DOTNET_VERSION: 8.x
+
jobs:
analyze:
name: Analyze
@@ -29,9 +32,28 @@ jobs:
with:
languages: ${{ matrix.language }}
- - name: Autobuild
+ - if: matrix.language != 'csharp'
+ name: Autobuild
uses: github/codeql-action/autobuild@v3
+ - if: matrix.language == 'csharp'
+ name: Setup .NET Core
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: ${{ env.DOTNET_VERSION }}
+
+ - if: matrix.language == 'csharp'
+ name: Build Patcher
+ run: |
+ export DOTNET_NOLOGO=true
+ export DOTNET_CLI_TELEMETRY_OPTOUT=true
+ cd $GITHUB_WORKSPACE/patcher
+ dotnet build HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -c Release -p:EnableWindowsTargeting=True
+ dotnet build HitmanPatcher.UI/HitmanPatcher.UI.csproj -c Release -p:EnableWindowsTargeting=True
+ dotnet build HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r linux-x64 -c "Release - Linux" -p:IsLinux=true
+ dotnet build HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -c Release -p:EnableWindowsTargeting=True -p:IsLegacy=True
+ dotnet build HitmanPatcher.UI/HitmanPatcher.UI.csproj -c Release -p:EnableWindowsTargeting=True -p:IsLegacy=True
+
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
diff --git a/.github/workflows/patcher.yml b/.github/workflows/patcher.yml
index 89fcb2f07..3806b5ace 100644
--- a/.github/workflows/patcher.yml
+++ b/.github/workflows/patcher.yml
@@ -8,23 +8,69 @@ on:
branches: [ "v*" ]
paths: [ "patcher/**/*" ]
+env:
+ DOTNET_VERSION: 8.x
+
jobs:
build:
name: Build Patcher
- runs-on: windows-latest
+ runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Add msbuild to PATH
- uses: microsoft/setup-msbuild@v2
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Build Patcher
- run: msbuild.exe patcher\HitmanPatcher.sln -t:Build -p:Configuration=Release -p:Platform=x64 -m
+ run: |
+ export DOTNET_NOLOGO=true
+ export DOTNET_CLI_TELEMETRY_OPTOUT=true
+ cd patcher
+ dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -c Release -p:PublishSingleFile=True --no-self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Portable
+ dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r win-x64 -c Release -f net8.0 -p:PublishTrimmed=True -p:PublishSingleFile=True --self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-x64
+ dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r linux-x64 -c "Release - Linux" -p:PublishSingleFile=True --no-self-contained -p DebugType=none -p:IsLinux=true -o Publish/Linux-Portable
+ dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r linux-x64 -c "Release - Linux" -f net8.0 -p:PublishTrimmed=True -p:PublishSingleFile=True --self-contained -p DebugType=none -p:IsLinux=true -o Publish/Linux-x64
+ dotnet publish HitmanPatcher.UI/HitmanPatcher.UI.csproj -c Release -p:PublishSingleFile=True --no-self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Portable
+ dotnet publish HitmanPatcher.UI/HitmanPatcher.UI.csproj -r win-x64 -c Release -p:PublishSingleFile=True --self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-x64
+ dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -c Release -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Legacy -p:IsLegacy=True
+ dotnet publish HitmanPatcher.UI/HitmanPatcher.UI.csproj -c Release -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Legacy -p:IsLegacy=True
+
+ - name: Upload patcher-windows-portable
+ uses: actions/upload-artifact@v4
+ with:
+ name: patcher-windows-portable
+ path: patcher/Publish/Windows-Portable
+
+ - name: Upload patcher-cli-windows
+ uses: actions/upload-artifact@v4
+ with:
+ name: patcher-cli-windows
+ path: patcher/Publish/Windows-x64/PeacockPatcher.CLI.exe
+
+ - name: Upload patcher-ui-windows
+ uses: actions/upload-artifact@v4
+ with:
+ name: patcher-ui-windows
+ path: patcher/Publish/Windows-x64/PeacockPatcher.UI.exe
+
+ - name: Upload patcher-linux-portable
+ uses: actions/upload-artifact@v4
+ with:
+ name: patcher-linux-portable
+ path: patcher/Publish/Linux-Portable
+
+ - name: Upload patcher-linux
+ uses: actions/upload-artifact@v4
+ with:
+ name: patcher-linux
+ path: patcher/Publish/Linux-x64
- - name: Upload Patcher Artifacts
+ - name: Upload patcher-windows-legacy
uses: actions/upload-artifact@v4
with:
- name: patcher-windows
- path: patcher/bin/x64/Release/PeacockPatcher.exe
+ name: patcher-windows-legacy
+ path: patcher/Publish/Windows-Legacy/ILRepack
diff --git a/patcher/.gitignore b/patcher/.gitignore
index 1747fa209..3d855743a 100644
--- a/patcher/.gitignore
+++ b/patcher/.gitignore
@@ -1,6 +1,9 @@
-.vs/*
-bin/*
-obj/*
+.vs
+bin
+obj
+/Publish
+/Res
/HitmanPatcher.sln.DotSettings.user
/.idea/.idea.HitmanPatcher/.idea/riderMarkupCache.xml
*.suo
+*.user
\ No newline at end of file
diff --git a/patcher/.idea/.idea.HitmanPatcher/.idea/.gitignore b/patcher/.idea/.idea.HitmanPatcher/.idea/.gitignore
index 81d8650d4..b0bc3ebd5 100644
--- a/patcher/.idea/.idea.HitmanPatcher/.idea/.gitignore
+++ b/patcher/.idea/.idea.HitmanPatcher/.idea/.gitignore
@@ -1,9 +1,13 @@
-# Default ignored files
+# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
+/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
-/projectSettingsUpdater.xml
/.idea.HitmanPatcher.iml
-discord.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/patcher/.idea/.idea.HitmanPatcher/.idea/copyright/AGPL_3_0.xml b/patcher/.idea/.idea.HitmanPatcher/.idea/copyright/AGPL_3_0.xml
index 0e3ba8f2e..716f85dad 100644
--- a/patcher/.idea/.idea.HitmanPatcher/.idea/copyright/AGPL_3_0.xml
+++ b/patcher/.idea/.idea.HitmanPatcher/.idea/copyright/AGPL_3_0.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/patcher/.idea/.idea.HitmanPatcher/.idea/encodings.xml b/patcher/.idea/.idea.HitmanPatcher/.idea/encodings.xml
new file mode 100644
index 000000000..df87cf951
--- /dev/null
+++ b/patcher/.idea/.idea.HitmanPatcher/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/patcher/App.config b/patcher/App.config
deleted file mode 100644
index fcf2f62a0..000000000
--- a/patcher/App.config
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/patcher/CliLocale.Designer.cs b/patcher/CliLocale.Designer.cs
deleted file mode 100644
index 679a1f453..000000000
--- a/patcher/CliLocale.Designer.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace HitmanPatcher {
- using System;
-
-
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class CliLocale {
-
- private static System.Resources.ResourceManager resourceMan;
-
- private static System.Globalization.CultureInfo resourceCulture;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal CliLocale() {
- }
-
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Resources.ResourceManager ResourceManager {
- get {
- if (object.Equals(null, resourceMan)) {
- System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HitmanPatcher.CliLocale", typeof(CliLocale).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- internal static string HeadlessDescription {
- get {
- return ResourceManager.GetString("HeadlessDescription", resourceCulture);
- }
- }
-
- internal static string DomainDescription {
- get {
- return ResourceManager.GetString("DomainDescription", resourceCulture);
- }
- }
-
- internal static string UseHttpDescription {
- get {
- return ResourceManager.GetString("UseHttpDescription", resourceCulture);
- }
- }
-
- internal static string OptionalDynResDescription {
- get {
- return ResourceManager.GetString("OptionalDynResDescription", resourceCulture);
- }
- }
-
- internal static string HeadlessBanner {
- get {
- return ResourceManager.GetString("HeadlessBanner", resourceCulture);
- }
- }
-
- internal static string HelpHeader {
- get {
- return ResourceManager.GetString("HelpHeader", resourceCulture);
- }
- }
- }
-}
diff --git a/patcher/CliLocale.resx b/patcher/CliLocale.resx
deleted file mode 100644
index b251cf8ee..000000000
--- a/patcher/CliLocale.resx
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 1.3
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- If the patcher should be run without the GUI.
-
-
- The domain for the game to connect to.
-
-
- If the game should connect using "http://" instead of the default, "https://".
-
-
- If the game should not force the user offline if the dynamic resources are missing or invalid.
-
-
- ====== Peacock Patcher - Headless mode ======
-
-
- Peacock Patcher CLI - Copyright (c) 2020-2024 grappigegovert & The Peacock Project
-
-
diff --git a/patcher/Cli.cs b/patcher/HitmanPatcher.CLI/Cli.cs
similarity index 71%
rename from patcher/Cli.cs
rename to patcher/HitmanPatcher.CLI/Cli.cs
index 413614f27..68490420a 100644
--- a/patcher/Cli.cs
+++ b/patcher/HitmanPatcher.CLI/Cli.cs
@@ -1,10 +1,3 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-#if !DEBUG
-using System.Linq;
-using System.Runtime.InteropServices;
-#endif
-
namespace HitmanPatcher
{
internal static class Cli
@@ -13,41 +6,30 @@ internal static class Cli
// in any mode outside debug (which is probably what it's being run in while in IDE),
// we attach the console to the parent process
- [DllImport("kernel32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool AttachConsole(int dwProcessId);
-
private const int ATTACH_PARENT_PROCESS = -1;
internal static void EnsureConsole(string[] args)
{
if (args.Any(arg => arg.Contains("-")))
{
- AttachConsole(ATTACH_PARENT_PROCESS);
+ Pinvoke.AttachConsole(ATTACH_PARENT_PROCESS);
}
}
#endif
internal class CliOptions
{
- internal bool Headless { get; set; }
-
internal string Domain { get; set; }
- internal bool UseHttp { get; set; }
+ internal bool? UseHttp { get; set; }
- internal bool OptionalDynRes { get; set; }
+ internal bool? OptionalDynRes { get; set; }
+
+ internal bool? KeepScanning { get; set; }
- [SuppressMessage("ReSharper", "LocalizableElement")]
internal static CliOptions FromArguments(string[] args)
{
- var options = new CliOptions
- {
- Domain = "127.0.0.1",
- Headless = false,
- OptionalDynRes = true,
- UseHttp = true
- };
+ var options = new CliOptions();
var i = 0;
@@ -63,9 +45,6 @@ internal static CliOptions FromArguments(string[] args)
{
switch (arg)
{
- case "--headless":
- options.Headless = true;
- break;
case "--optional-dynamic-resources":
options.OptionalDynRes = true;
break;
@@ -76,14 +55,17 @@ internal static CliOptions FromArguments(string[] args)
case "--use-http":
options.UseHttp = true;
break;
+ case "--keep-scanning":
+ options.KeepScanning = true;
+ break;
case "--help":
Console.WriteLine(CliLocale.HelpHeader);
Console.WriteLine("");
Console.WriteLine("Options:");
- Console.WriteLine($" --headless : {CliLocale.HeadlessDescription}");
Console.WriteLine($" --optional-dynamic-resources : {CliLocale.OptionalDynResDescription}");
Console.WriteLine($" --domain : {CliLocale.DomainDescription}");
Console.WriteLine($" --use-http : {CliLocale.UseHttpDescription}");
+ Console.WriteLine($" --keep-scanning : {CliLocale.KeepScanning}");
Environment.Exit(0);
break;
}
diff --git a/patcher/HitmanPatcher.CLI/CliLocale.Designer.cs b/patcher/HitmanPatcher.CLI/CliLocale.Designer.cs
new file mode 100644
index 000000000..c8893e013
--- /dev/null
+++ b/patcher/HitmanPatcher.CLI/CliLocale.Designer.cs
@@ -0,0 +1,117 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace HitmanPatcher {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class CliLocale {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal CliLocale() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HitmanPatcher.CliLocale", typeof(CliLocale).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The domain for the game to connect to..
+ ///
+ internal static string DomainDescription {
+ get {
+ return ResourceManager.GetString("DomainDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ====== Peacock Patcher - Headless mode ======.
+ ///
+ internal static string HeadlessBanner {
+ get {
+ return ResourceManager.GetString("HeadlessBanner", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Peacock Patcher CLI - Copyright (c) 2020-2024 grappigegovert & The Peacock Project.
+ ///
+ internal static string HelpHeader {
+ get {
+ return ResourceManager.GetString("HelpHeader", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to If the patcher should keep scanning for processes after patching..
+ ///
+ internal static string KeepScanning {
+ get {
+ return ResourceManager.GetString("KeepScanning", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to If the game should not force the user offline if the dynamic resources are missing or invalid..
+ ///
+ internal static string OptionalDynResDescription {
+ get {
+ return ResourceManager.GetString("OptionalDynResDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to If the game should connect using "http://" instead of the default, "https://"..
+ ///
+ internal static string UseHttpDescription {
+ get {
+ return ResourceManager.GetString("UseHttpDescription", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/patcher/HitmanPatcher.CLI/CliLocale.resx b/patcher/HitmanPatcher.CLI/CliLocale.resx
new file mode 100644
index 000000000..11c75da8e
--- /dev/null
+++ b/patcher/HitmanPatcher.CLI/CliLocale.resx
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ If the patcher should keep scanning for processes after patching.
+
+
+ The domain for the game to connect to.
+
+
+ If the game should connect using "http://" instead of the default, "https://".
+
+
+ If the game should not force the user offline if the dynamic resources are missing or invalid.
+
+
+ ====== Peacock Patcher - Headless mode ======
+
+
+ Peacock Patcher CLI - Copyright (c) 2020-2024 grappigegovert & The Peacock Project
+
+
\ No newline at end of file
diff --git a/patcher/HitmanPatcher.CLI/HitmanPatcher.CLI.csproj b/patcher/HitmanPatcher.CLI/HitmanPatcher.CLI.csproj
new file mode 100644
index 000000000..84a0ec1ef
--- /dev/null
+++ b/patcher/HitmanPatcher.CLI/HitmanPatcher.CLI.csproj
@@ -0,0 +1,59 @@
+
+
+ Exe
+ net8.0
+ net46
+ enable
+ disable
+
+ HitmanPatcher
+ PeacockPatcher.CLI
+
+ Debug;Release;Debug - Linux;Release - Linux
+
+ Peacock Patcher
+ Peacock's HITMAN™ World of Assassination trilogy game patcher.
+ The Peacock Project
+ Peacock Patcher
+ Copyright © 2020-2024 grappigegovert & The Peacock Project
+ true
+ DpiUnawareGdiScaled
+ patcher.ico
+ 8.0.0.0
+ 8.0.0.0
+
+ latest
+
+
+
+ $(DefineConstants);LINUX;DEBUG
+
+
+
+ $(DefineConstants);LINUX
+ True
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+ CliLocale.resx
+ True
+ True
+
+
+ CliLocale.Designer.cs
+ ResXFileCodeGenerator
+
+
+
\ No newline at end of file
diff --git a/patcher/HitmanPatcher.CLI/ILRepack.targets b/patcher/HitmanPatcher.CLI/ILRepack.targets
new file mode 100644
index 000000000..5ffd50f69
--- /dev/null
+++ b/patcher/HitmanPatcher.CLI/ILRepack.targets
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/patcher/HitmanPatcher.CLI/Program.cs b/patcher/HitmanPatcher.CLI/Program.cs
new file mode 100644
index 000000000..11c8055f9
--- /dev/null
+++ b/patcher/HitmanPatcher.CLI/Program.cs
@@ -0,0 +1,54 @@
+namespace HitmanPatcher
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ private static void Main(string[] args)
+ {
+ Compositions.Logger = new Cli.ConsoleLogger();
+
+#if !DEBUG
+ Cli.EnsureConsole(args);
+#endif
+
+ Cli.CliOptions o = Cli.CliOptions.FromArguments(args);
+
+ // ReSharper disable once LocalizableElement
+ Console.WriteLine(CliLocale.HeadlessBanner);
+
+ var settings = Settings.GetFromFile();
+ settings.SaveToFile();
+
+ Console.WriteLine("Settings:");
+ Console.WriteLine($"- File = {Path.GetFullPath(Settings.GetSavePath())}");
+ Console.WriteLine($"- CustomConfigDomain = {settings.patchOptions.CustomConfigDomain}");
+ Console.WriteLine($"- UseHttp = {settings.patchOptions.UseHttp}");
+ Console.WriteLine($"- DisableForceDynamicResources = {settings.patchOptions.DisableForceOfflineOnFailedDynamicResources}");
+ Console.WriteLine();
+
+ // Set the active values
+ settings.patchOptions.CustomConfigDomain = o.Domain ?? settings.patchOptions.CustomConfigDomain;
+ settings.patchOptions.DisableForceOfflineOnFailedDynamicResources = o.OptionalDynRes ?? settings.patchOptions.DisableForceOfflineOnFailedDynamicResources;
+ settings.patchOptions.UseHttp = o.UseHttp ?? settings.patchOptions.UseHttp;
+
+ var keepScanning = o.KeepScanning ?? false;
+
+ while (true)
+ {
+ var result = MemoryPatcher.PatchAllProcesses(Compositions.Logger, settings.patchOptions);
+
+ if (result && !keepScanning)
+ {
+ break;
+ }
+
+ Console.WriteLine("Waiting...");
+
+ Thread.Sleep(1000);
+ }
+ }
+ }
+}
diff --git a/patcher/patcher.ico b/patcher/HitmanPatcher.CLI/patcher.ico
similarity index 100%
rename from patcher/patcher.ico
rename to patcher/HitmanPatcher.CLI/patcher.ico
diff --git a/patcher/AOBScanner.cs b/patcher/HitmanPatcher.Core/AOBScanner.cs
similarity index 97%
rename from patcher/AOBScanner.cs
rename to patcher/HitmanPatcher.Core/AOBScanner.cs
index 89b9ee49c..97b5ed160 100644
--- a/patcher/AOBScanner.cs
+++ b/patcher/HitmanPatcher.Core/AOBScanner.cs
@@ -1,20 +1,16 @@
-using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Linq;
-using System.Threading.Tasks;
namespace HitmanPatcher
{
internal static class AOBScanner
{
- public static bool TryGetHitmanVersionByScanning(Process process,
+ public static bool TryGetHitmanVersionByScanning(ProcessMetadata processMetadata,
IntPtr hProcess, out HitmanVersion result)
{
Stopwatch bench = Stopwatch.StartNew();
- IntPtr baseAddress = process.MainModule.BaseAddress;
- byte[] exeData = new byte[process.MainModule.ModuleMemorySize];
+ IntPtr baseAddress = processMetadata.BaseAddress;
+ byte[] exeData = new byte[processMetadata.ModuleMemorySize];
Pinvoke.ReadProcessMemory(hProcess, baseAddress, exeData,
(UIntPtr) exeData.Length,
out _); // fuck it, just read the whole thing
@@ -77,7 +73,7 @@ public static bool TryGetHitmanVersionByScanning(Process process,
Task.WaitAll(alltasks);
bench.Stop();
- Console.WriteLine(bench.Elapsed.ToString());
+ Compositions.Logger.log(bench.Elapsed.ToString());
// error out if any task does not have exactly 1 result
if (alltasks.Any(task => task.Result.Count() != 1))
@@ -113,7 +109,7 @@ public static bool TryGetHitmanVersionByScanning(Process process,
#if DEBUG
private static void Note(string name, Patch patch)
{
- MainForm.GetInstance().log($"{name}: {patch.offset:X} {BitConverter.ToString(patch.original).Replace("-", string.Empty)} {BitConverter.ToString(patch.patch).Replace("-", string.Empty)}");
+ Compositions.Logger.log($"{name}: {patch.offset:X} {BitConverter.ToString(patch.original).Replace("-", string.Empty)} {BitConverter.ToString(patch.patch).Replace("-", string.Empty)}");
}
#endif
diff --git a/patcher/HitmanPatcher.Core/Compositions.cs b/patcher/HitmanPatcher.Core/Compositions.cs
new file mode 100644
index 000000000..e6e1fc715
--- /dev/null
+++ b/patcher/HitmanPatcher.Core/Compositions.cs
@@ -0,0 +1,10 @@
+namespace HitmanPatcher
+{
+ public static class Compositions
+ {
+ //NOTE: This will only have to be determined once
+ public static bool HasAdmin { get; } = Pinvoke.CheckForAdmin();
+
+ public static ILoggingProvider Logger { get; set; }
+ }
+}
diff --git a/patcher/HitmanPatcher.Core/HitmanPatcher.Core.csproj b/patcher/HitmanPatcher.Core/HitmanPatcher.Core.csproj
new file mode 100644
index 000000000..414ddfb3b
--- /dev/null
+++ b/patcher/HitmanPatcher.Core/HitmanPatcher.Core.csproj
@@ -0,0 +1,30 @@
+
+
+ net8.0
+ net46
+ enable
+ disable
+
+ HitmanPatcher
+ PeacockPatcher.Core
+
+ Debug;Release;Debug - Linux;Release - Linux
+
+ latest
+
+
+
+ CA1416
+
+
+
+ $(DefineConstants);LINUX;DEBUG
+ true
+
+
+
+ $(DefineConstants);LINUX
+ true
+ True
+
+
diff --git a/patcher/HitmanVersion.cs b/patcher/HitmanPatcher.Core/HitmanVersion.cs
similarity index 86%
rename from patcher/HitmanVersion.cs
rename to patcher/HitmanPatcher.Core/HitmanVersion.cs
index 24ac7dc77..72c451dd4 100644
--- a/patcher/HitmanVersion.cs
+++ b/patcher/HitmanPatcher.Core/HitmanVersion.cs
@@ -1,11 +1,19 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.Remoting.Metadata.W3cXsd2001;
using System.Text;
using HitmanPatcher.PatchDefinitions;
namespace HitmanPatcher
{
+ public static class SoapHexBinary
+ {
+ public static byte[] Parse(string value)
+ {
+ return Enumerable.Range(0, value.Length)
+ .Where(x => x % 2 == 0)
+ .Select(x => Convert.ToByte(value.Substring(x, 2), 16))
+ .ToArray();
+ }
+ }
+
public class Patch
{
public static readonly byte[] http = Encoding.ASCII.GetBytes("http://{0}\0").ToArray();
@@ -26,7 +34,7 @@ public Patch(int offset, byte[] original, byte[] patch, MemProtection defaultPro
}
public Patch(int offset, string original, string patch, MemProtection defaultProtection, string customPatch = "")
- : this(offset, SoapHexBinary.Parse(original).Value, SoapHexBinary.Parse(patch).Value, defaultProtection, customPatch)
+ : this(offset, SoapHexBinary.Parse(original), SoapHexBinary.Parse(patch), defaultProtection, customPatch)
{
}
diff --git a/patcher/MemoryPatcher.cs b/patcher/HitmanPatcher.Core/MemoryPatcher.cs
similarity index 76%
rename from patcher/MemoryPatcher.cs
rename to patcher/HitmanPatcher.Core/MemoryPatcher.cs
index 668556b30..f34e8840c 100644
--- a/patcher/MemoryPatcher.cs
+++ b/patcher/HitmanPatcher.Core/MemoryPatcher.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@@ -35,7 +35,7 @@ private static List GetProcessesByName(params string[] names)
{
try
{
- if (names.Contains(p.ProcessName, StringComparer.OrdinalIgnoreCase))
+ if (names.Contains(Path.GetFileNameWithoutExtension(p.ProcessName), StringComparer.OrdinalIgnoreCase))
{
result.Add(p);
}
@@ -52,8 +52,10 @@ private static List GetProcessesByName(params string[] names)
return result;
}
- public static void PatchAllProcesses(ILoggingProvider logger, Options patchOptions)
- {
+ public static bool PatchAllProcesses(ILoggingProvider logger, Options patchOptions)
+ {
+ bool patched = false;
+
IEnumerable hitmans = GetProcessesByName("HITMAN", "HITMAN2", "HITMAN3");
foreach (Process process in hitmans)
{
@@ -69,7 +71,7 @@ public static void PatchAllProcesses(ILoggingProvider logger, Options patchOptio
}
catch (Win32Exception ex)
{
- if (ex.NativeErrorCode == 5 && !Program.HasAdmin)
+ if (ex.NativeErrorCode == 5 && !Compositions.HasAdmin)
{
logger.log(String.Format("Access denied, try running the patcher as admin."));
process.Dispose();
@@ -95,7 +97,9 @@ public static void PatchAllProcesses(ILoggingProvider logger, Options patchOptio
{
logger.log(String.Format("Injected server: {0}", patchOptions.CustomConfigDomain));
}
- }
+
+ patched = true;
+ }
else
{
// else: process not yet ready for patching, try again next timer tick
@@ -114,39 +118,37 @@ public static void PatchAllProcesses(ILoggingProvider logger, Options patchOptio
}
process.Dispose();
}
- }
+
+ return patched;
+ }
public static bool Patch(Process process, Options patchOptions)
{
- IntPtr hProcess = Pinvoke.OpenProcess(
+ var processMetadata = Pinvoke.GetProcessMetadata(process);
+
+ if (processMetadata.BaseAddress == IntPtr.Zero || processMetadata.ModuleMemorySize == 0)
+ {
+ return false; // process has no main module (not initialized yet?), try again next timer tick.
+ }
+
+ IntPtr hProcess = Pinvoke.OpenProcess(
ProcessAccess.PROCESS_VM_READ
| ProcessAccess.PROCESS_VM_WRITE
| ProcessAccess.PROCESS_VM_OPERATION,
- false, process.Id);
+ false, processMetadata.PID);
if (hProcess == IntPtr.Zero)
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get a process handle.");
+ throw new Win32Exception(Pinvoke.LastError, "Failed to get a process handle.");
}
try
{
- IntPtr b = IntPtr.Zero;
- try
- {
- ProcessModule mainModule = process.MainModule;
- b = mainModule.BaseAddress;
- }
- catch (NullReferenceException)
- {
- return false; // process has no main module (not initialized yet?), try again next timer tick.
- }
-
- uint timestamp = getTimestamp(hProcess, b);
+ uint timestamp = getTimestamp(hProcess, processMetadata.BaseAddress);
HitmanVersion v = HitmanVersion.GetVersion(timestamp);
if (v == HitmanVersion.NotFound)
{
- if (AOBScanner.TryGetHitmanVersionByScanning(process, hProcess, out v))
+ if (AOBScanner.TryGetHitmanVersionByScanning(processMetadata, hProcess, out v))
{
// add it to the db so subsequent patches don't need searching again
HitmanVersion.AddVersion(timestamp.ToString("X8"), timestamp, v);
@@ -161,7 +163,7 @@ public static bool Patch(Process process, Options patchOptions)
byte[] newurl = Encoding.ASCII.GetBytes(patchOptions.CustomConfigDomain).Concat(new byte[] { 0x00 }).ToArray();
List patches = new List();
- if (!IsReadyForPatching(hProcess, b, v))
+ if (!IsReadyForPatching(hProcess, processMetadata.BaseAddress, v))
{
// Online_ConfigDomain variable is not initialized yet, try again in 1 second.
Pinvoke.CloseHandle(hProcess);
@@ -212,24 +214,24 @@ public static bool Patch(Process process, Options patchOptions)
throw new Exception("This shouldn't be able to happen.");
}
- if (!Pinvoke.VirtualProtectEx(hProcess, b + patch.offset, (UIntPtr)dataToWrite.Length,
+ if (!Pinvoke.VirtualProtectEx(hProcess, processMetadata.BaseAddress + patch.offset, (UIntPtr)dataToWrite.Length,
newmemprotection, out oldprotectflags))
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format("error at {0} for offset {1:X}", "vpe1", patch.offset));
+ throw new Win32Exception(Pinvoke.LastError, string.Format("error at {0} for offset {1:X}", "vpe1", patch.offset));
}
- if (!Pinvoke.WriteProcessMemory(hProcess, b + patch.offset, dataToWrite, (UIntPtr)dataToWrite.Length, out byteswritten))
+ if (!Pinvoke.WriteProcessMemory(hProcess, processMetadata.BaseAddress + patch.offset, dataToWrite, (UIntPtr)dataToWrite.Length, out byteswritten))
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format("error at {0} for offset {1:X}"
+ throw new Win32Exception(Pinvoke.LastError, string.Format("error at {0} for offset {1:X}"
+ "\nBytes written: {2}", "wpm", patch.offset, byteswritten));
}
MemProtection protectionToRestore = oldprotectflags;
- if (!Pinvoke.VirtualProtectEx(hProcess, b + patch.offset, (UIntPtr)dataToWrite.Length,
+ if (!Pinvoke.VirtualProtectEx(hProcess, processMetadata.BaseAddress + patch.offset, (UIntPtr)dataToWrite.Length,
protectionToRestore, out oldprotectflags))
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format("error at {0} for offset {1:X}", "vpe2", patch.offset));
+ throw new Win32Exception(Pinvoke.LastError, string.Format("error at {0} for offset {1:X}", "vpe2", patch.offset));
}
}
}
@@ -253,15 +255,15 @@ private static bool IsReadyForPatching(IntPtr hProcess, IntPtr baseAddress, Hitm
MemProtection oldprotectflags;
if (!Pinvoke.VirtualProtectEx(hProcess, baseAddress + p.offset, (UIntPtr)1, newmemprotection, out oldprotectflags))
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format("error at vpe1Check for offset {0:X}", p.offset));
+ throw new Win32Exception(Pinvoke.LastError, string.Format("error at vpe1Check for offset {0:X}", p.offset));
}
if (!Pinvoke.ReadProcessMemory(hProcess, baseAddress + p.offset, buffer, (UIntPtr)1, out bytesread))
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format("error at rpmCheck for offset {0:X}", p.offset));
+ throw new Win32Exception(Pinvoke.LastError, string.Format("error at rpmCheck for offset {0:X}", p.offset));
}
if (!Pinvoke.VirtualProtectEx(hProcess, baseAddress + p.offset, (UIntPtr)1, oldprotectflags, out oldprotectflags))
{
- throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format("error at vpe2Check for offset {0:X}", p.offset));
+ throw new Win32Exception(Pinvoke.LastError, string.Format("error at vpe2Check for offset {0:X}", p.offset));
}
ready &= buffer[0] != 0;
}
diff --git a/patcher/PatchDefinitions/v1_12.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v1_12.cs
similarity index 100%
rename from patcher/PatchDefinitions/v1_12.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v1_12.cs
diff --git a/patcher/PatchDefinitions/v1_15.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v1_15.cs
similarity index 100%
rename from patcher/PatchDefinitions/v1_15.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v1_15.cs
diff --git a/patcher/PatchDefinitions/v1_16.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v1_16.cs
similarity index 100%
rename from patcher/PatchDefinitions/v1_16.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v1_16.cs
diff --git a/patcher/PatchDefinitions/v2_13.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v2_13.cs
similarity index 100%
rename from patcher/PatchDefinitions/v2_13.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v2_13.cs
diff --git a/patcher/PatchDefinitions/v2_71.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v2_71.cs
similarity index 100%
rename from patcher/PatchDefinitions/v2_71.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v2_71.cs
diff --git a/patcher/PatchDefinitions/v2_72.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v2_72.cs
similarity index 100%
rename from patcher/PatchDefinitions/v2_72.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v2_72.cs
diff --git a/patcher/PatchDefinitions/v3_10.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_10.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_10.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_10.cs
diff --git a/patcher/PatchDefinitions/v3_100.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_100.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_100.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_100.cs
diff --git a/patcher/PatchDefinitions/v3_11.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_11.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_11.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_11.cs
diff --git a/patcher/PatchDefinitions/v3_110.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_110.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_110.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_110.cs
diff --git a/patcher/PatchDefinitions/v3_120.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_120.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_120.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_120.cs
diff --git a/patcher/PatchDefinitions/v3_20.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_20.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_20.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_20.cs
diff --git a/patcher/PatchDefinitions/v3_30.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_30.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_30.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_30.cs
diff --git a/patcher/PatchDefinitions/v3_40.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_40.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_40.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_40.cs
diff --git a/patcher/PatchDefinitions/v3_50.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_50.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_50.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_50.cs
diff --git a/patcher/PatchDefinitions/v3_70.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/v3_70.cs
similarity index 100%
rename from patcher/PatchDefinitions/v3_70.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/v3_70.cs
diff --git a/patcher/PatchDefinitions/vScpc.cs b/patcher/HitmanPatcher.Core/PatchDefinitions/vScpc.cs
similarity index 100%
rename from patcher/PatchDefinitions/vScpc.cs
rename to patcher/HitmanPatcher.Core/PatchDefinitions/vScpc.cs
diff --git a/patcher/HitmanPatcher.Core/Pinvoke.Linux.cs b/patcher/HitmanPatcher.Core/Pinvoke.Linux.cs
new file mode 100644
index 000000000..09b769187
--- /dev/null
+++ b/patcher/HitmanPatcher.Core/Pinvoke.Linux.cs
@@ -0,0 +1,286 @@
+#if LINUX
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+
+namespace HitmanPatcher;
+
+public static partial class Pinvoke
+{
+ [StructLayout(LayoutKind.Sequential)]
+#pragma warning disable CS8981
+ private unsafe struct iovec
+#pragma warning restore CS8981
+ {
+ public void* iov_base;
+ public int iov_len;
+ }
+
+ private enum PtraceRequest
+ {
+ //PTRACE_PEEKDATA = 2,
+ PTRACE_POKEDATA = 5,
+ PTRACE_ATTACH = 16,
+ PTRACE_DETACH = 17
+ }
+
+ [DllImport("libc", SetLastError = true)]
+ private static extern unsafe long ptrace(PtraceRequest request, int pid, void* address, void* data);
+
+ [DllImport("libc", SetLastError = true)]
+ private static extern unsafe int process_vm_readv(int pid, iovec* local_iov, ulong liovcnt, iovec* remote_iov, ulong riovcnt, ulong flags);
+
+ private static readonly Regex _modulePathRegex = new Regex("\\/.*?$", RegexOptions.Compiled);
+
+ public static ProcessMetadata GetProcessMetadata(Process process)
+ {
+ var lines = File.ReadAllLines($"/proc/{process.Id}/maps");
+
+ var findProcess = process.ProcessName.ToLower();
+ var foundModule = lines.First(x => x.Contains(findProcess, StringComparison.CurrentCultureIgnoreCase));
+ var foundModulePath = _modulePathRegex.Match(foundModule).Value;
+ var (baseAddress, imageSize) = GetImageSizeFromPE(foundModulePath);
+
+ return new ProcessMetadata
+ {
+ PID = process.Id,
+ BaseAddress = new IntPtr((long) baseAddress),
+ ModuleMemorySize = imageSize
+ };
+ }
+
+ /**
+ * Attach to the given process using ptrace, return the PID as the handle.
+ */
+ public static unsafe IntPtr OpenProcess(ProcessAccess dwDesiredAccess, bool bInheritHandle, int dwProcessId)
+ {
+ Compositions.Logger.log("Open: " + dwProcessId);
+
+ var result = ptrace(PtraceRequest.PTRACE_ATTACH, dwProcessId, null, null);
+
+ if (result == -1)
+ {
+ Compositions.Logger.log($"- Attach error: {LastError}");
+
+ return IntPtr.Zero;
+ }
+
+ Compositions.Logger.log("- Attach: " + result);
+
+ return new IntPtr(dwProcessId);
+ }
+
+ /**
+ * Retrieve the PID from the handle, and detach from the process using ptrace.
+ */
+ public static unsafe void CloseHandle(IntPtr hProcess)
+ {
+ var pid = hProcess.ToInt32();
+
+ Compositions.Logger.log($"Close: {pid}");
+
+ var result = ptrace(PtraceRequest.PTRACE_DETACH, pid, null, null);
+
+ if (result == -1)
+ {
+ Compositions.Logger.log($"- Detach error: {LastError}");
+ }
+ else
+ {
+ Compositions.Logger.log($"- Detach: {result}");
+ }
+ }
+
+ /**
+ * Since process_vm_writev is too respectful of permissions, we have to use ptrace to write to memory.
+ *
+ * ptrace writes data in blocks of 8 bytes (long), so if a given buffer is non-divisible by 8, we have to pad it with data read from memory to prevent corruption.
+ */
+ public static unsafe bool WriteProcessMemory(IntPtr hProcess, IntPtr address, byte[] buffer, UIntPtr size, out UIntPtr numberOfBytesWritten)
+ {
+ var pid = hProcess.ToInt32();
+ var sizeAsUInt = (int)size.ToUInt32();
+
+ Compositions.Logger.log($"Write: {pid} {sizeAsUInt}");
+
+ var desiredBufferSize = (int)Math.Ceiling(sizeAsUInt / 8.0) * 8;
+
+ byte[] paddedBuffer;
+
+ if (sizeAsUInt < desiredBufferSize)
+ {
+ Compositions.Logger.log($"- Padding buffer to {desiredBufferSize}");
+
+ paddedBuffer = new byte[desiredBufferSize];
+ ReadProcessMemory(hProcess, address, paddedBuffer, new UIntPtr((uint)desiredBufferSize), out _);
+
+ for (var i = 0; i < sizeAsUInt; i++)
+ {
+ paddedBuffer[i] = buffer[i];
+ }
+ }
+ else
+ {
+ paddedBuffer = buffer;
+ }
+
+ Compositions.Logger.log($"- Padded: {BitConverter.ToString(paddedBuffer)}");
+
+ var debugBuffer = new byte[paddedBuffer.Length];
+ ReadProcessMemory(hProcess, address, debugBuffer, new UIntPtr((uint)debugBuffer.Length), out _);
+ Compositions.Logger.log($"- Debug before: {BitConverter.ToString(debugBuffer)}");
+
+ for (int i = 0; i < desiredBufferSize; i += 8)
+ {
+ var value = BitConverter.ToInt64(paddedBuffer, i);
+
+ var result = ptrace(PtraceRequest.PTRACE_POKEDATA, pid, (address + i).ToPointer(), (void*)value);
+
+ if (result == -1)
+ {
+ Compositions.Logger.log($"- Poke error: {LastError}");
+
+ numberOfBytesWritten = UIntPtr.Zero;
+
+ return false;
+ }
+
+ Compositions.Logger.log($"- Poke: {result}");
+ }
+
+ debugBuffer = new byte[paddedBuffer.Length];
+ ReadProcessMemory(hProcess, address, debugBuffer, new UIntPtr((uint)debugBuffer.Length), out _);
+ Compositions.Logger.log("- Debug after: " + BitConverter.ToString(debugBuffer));
+
+ numberOfBytesWritten = size;
+
+ return true;
+ }
+
+ /**
+ * Memory can always be read regardless of permissions, so we can use process_vm_readv.
+ *
+ * To prevent stack overflows, memory is read in chunks of 1MB.
+ */
+ public static unsafe bool ReadProcessMemory(IntPtr hProcess, IntPtr address, byte[] buffer, UIntPtr size, out UIntPtr numberOfBytesRead)
+ {
+ var pid = hProcess.ToInt32();
+ var sizeAsUInt = (int)size.ToUInt32();
+
+ Compositions.Logger.log($"Read: {pid} {sizeAsUInt}");
+
+ var chunkSize = Math.Min(sizeAsUInt, 1_000_000);
+
+ var ptr = stackalloc byte[chunkSize];
+
+ for (var i = 0; i < sizeAsUInt; i += chunkSize)
+ {
+ if (sizeAsUInt - i < chunkSize)
+ {
+ chunkSize = sizeAsUInt - i;
+ }
+
+ Compositions.Logger.log($"- Chunk: {i}/{sizeAsUInt} => {chunkSize}");
+
+ var localIo = new iovec
+ {
+ iov_base = ptr,
+ iov_len = chunkSize
+ };
+
+ var remoteIo = new iovec
+ {
+ iov_base = (address + i).ToPointer(),
+ iov_len = chunkSize
+ };
+
+ var res = process_vm_readv(pid, &localIo, 1, &remoteIo, 1, 0);
+
+ if (res == -1)
+ {
+ numberOfBytesRead = UIntPtr.Zero;
+
+ return false;
+ }
+
+ Marshal.Copy((IntPtr)ptr, buffer, i, chunkSize);
+ }
+
+ numberOfBytesRead = new UIntPtr((uint)sizeAsUInt);
+
+ return true;
+ }
+
+ /**
+ * Technically ptrace attach/detach would be the closest equivalent, but from a performance perspective, we only do that once.
+ */
+ public static bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, MemProtection flNewProtect, out MemProtection lpflOldProtect)
+ {
+ lpflOldProtect = 0;
+
+ return true;
+ }
+
+ public static void AttachConsole(int attachParentProcess)
+ {
+ //Do nothing
+ }
+
+ public static bool CheckForAdmin()
+ {
+ //TODO: Check if SUDO_USER is set
+
+ return false;
+ }
+
+ public static int GetProcessParentPid(Process process)
+ {
+ //TODO: Retrieve "PPid" from "/proc/pid/status"
+
+ return -1;
+ }
+
+ private static (ulong, uint) GetImageSizeFromPE(string modulePath)
+ {
+ using var module = File.OpenRead(modulePath);
+ using var reader = new BinaryReader(module);
+
+ //Validate DOS Header (MZ)
+ var dosHeader = reader.ReadUInt16();
+
+ if(dosHeader != 0x5A4D) {
+ return (0, 0);
+ }
+
+ //Skip to PE offset
+ reader.BaseStream.Seek(0x3A, SeekOrigin.Current);
+ var peOffset = reader.ReadInt32();
+
+ //Validate PE Header (PE\0\0)
+ reader.BaseStream.Seek(peOffset, SeekOrigin.Begin);
+ var peHeader = reader.ReadUInt32();
+
+ if(peHeader != 0x00004550) {
+ return (0, 0);
+ }
+
+ //Validate PE Type (PE32+)
+ reader.BaseStream.Seek(20, SeekOrigin.Current);
+ var peType = reader.ReadUInt16();
+
+ if(peType != 0x020B) {
+ return (0, 0);
+ }
+
+ //Skip to base address
+ reader.BaseStream.Seek(22, SeekOrigin.Current);
+ var baseAddress = reader.ReadUInt64();
+
+ //Skip to image size
+ reader.BaseStream.Seek(24, SeekOrigin.Current);
+ var imageSize = reader.ReadUInt32();
+
+ return (baseAddress, imageSize);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/patcher/HitmanPatcher.Core/Pinvoke.Windows.cs b/patcher/HitmanPatcher.Core/Pinvoke.Windows.cs
new file mode 100644
index 000000000..497668349
--- /dev/null
+++ b/patcher/HitmanPatcher.Core/Pinvoke.Windows.cs
@@ -0,0 +1,103 @@
+#if !LINUX
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+
+namespace HitmanPatcher
+{
+ public static partial class Pinvoke
+ {
+ //Source: https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
+ private enum PROCESSINFOCLASS
+ {
+ ProcessBasicInformation = 0,
+ ProcessDebugPort = 7,
+ ProcessWow64Information = 26,
+ ProcessImageFileName = 27,
+ ProcessBreakOnTermination = 29,
+ ProcessSubsystemInformation = 75
+ }
+
+ //Source: https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
+ [StructLayout(LayoutKind.Sequential)]
+ private struct PROCESS_BASIC_INFORMATION
+ {
+ public IntPtr Reserved1;
+ public IntPtr PebBaseAddress;
+ public IntPtr Reserved2_0;
+ public IntPtr Reserved2_1;
+ public IntPtr UniqueProcessId;
+ public IntPtr Reserved3;
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool AttachConsole(int dwProcessId);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool WriteProcessMemory([In] IntPtr hProcess, [In] IntPtr address, [In, MarshalAs(UnmanagedType.LPArray)] byte[] buffer, [In] UIntPtr size, [Out] out UIntPtr byteswritten);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool ReadProcessMemory([In] IntPtr hProcess, [In] IntPtr address, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] buffer, [In] UIntPtr size, [Out] out UIntPtr numberOfBytesRead);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool VirtualProtectEx([In] IntPtr hProcess, [In] IntPtr lpAddress, [In] UIntPtr dwSize, [In] MemProtection flNewProtect, [Out] out MemProtection lpflOldProtect);
+
+ [DllImport("ntdll.dll")]
+ private static extern int NtQueryInformationProcess(IntPtr hProcess, PROCESSINFOCLASS processInformationClass, out PROCESS_BASIC_INFORMATION processInformation, uint processInformationLength, out uint returnLength);
+
+ public static bool CheckForAdmin()
+ {
+ using var identity = WindowsIdentity.GetCurrent();
+
+ var principal = new WindowsPrincipal(identity);
+
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+
+ public static int GetProcessParentPid(Process process)
+ {
+ IntPtr hProcess = OpenProcess(
+ ProcessAccess.PROCESS_VM_READ
+ | ProcessAccess.PROCESS_QUERY_INFORMATION,
+ false, process.Id);
+
+ if (hProcess == IntPtr.Zero)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get a process handle.");
+ }
+
+ PROCESS_BASIC_INFORMATION PEB = new PROCESS_BASIC_INFORMATION();
+
+ int result = NtQueryInformationProcess(hProcess,
+ PROCESSINFOCLASS.ProcessBasicInformation, out PEB,
+ (uint)Marshal.SizeOf(PEB), out _);
+
+ CloseHandle(hProcess);
+ if (result != 0)
+ {
+ throw new Win32Exception(result, "(NTSTATUS)"); // not a w32 status code, but an NTSTATUS
+ }
+
+ return PEB.Reserved3.ToInt32(); // undocumented, but should hold the parent PID
+ }
+
+ public static ProcessMetadata GetProcessMetadata(Process process)
+ {
+ return new ProcessMetadata
+ {
+ PID = process.Id,
+ BaseAddress = process.MainModule?.BaseAddress ?? IntPtr.Zero,
+ ModuleMemorySize = process.MainModule?.ModuleMemorySize ?? 0
+ };
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/patcher/HitmanPatcher.Core/Pinvoke.cs b/patcher/HitmanPatcher.Core/Pinvoke.cs
new file mode 100644
index 000000000..7001b2051
--- /dev/null
+++ b/patcher/HitmanPatcher.Core/Pinvoke.cs
@@ -0,0 +1,43 @@
+using System.Runtime.InteropServices;
+
+namespace HitmanPatcher
+{
+ public class ProcessMetadata
+ {
+ public int PID { get; set; }
+ public IntPtr BaseAddress { get; set; }
+ public long ModuleMemorySize { get; set; }
+ }
+
+ //Source: https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
+ [Flags]
+ public enum MemProtection : uint
+ {
+ PAGE_EXECUTE = 0x00000010,
+ PAGE_EXECUTE_READ = 0x00000020,
+ PAGE_EXECUTE_READWRITE = 0x00000040,
+ PAGE_EXECUTE_WRITECOPY = 0x00000080,
+ PAGE_NOACCESS = 0x00000001,
+ PAGE_READONLY = 0x00000002,
+ PAGE_READWRITE = 0x00000004,
+ PAGE_WRITECOPY = 0x00000008,
+ PAGE_GUARD = 0x00000100,
+ PAGE_NOCACHE = 0x00000200,
+ PAGE_WRITECOMBINE = 0x00000400
+ }
+
+ //Source: https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
+ [Flags]
+ public enum ProcessAccess : uint
+ {
+ PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process.
+ PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
+ PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
+ PROCESS_VM_OPERATION = 0x0008 // Required to perform an operation on the address space of a process using VirtualProtectEx
+ }
+
+ public static partial class Pinvoke
+ {
+ public static int LastError => Marshal.GetLastWin32Error();
+ }
+}
diff --git a/patcher/Settings.cs b/patcher/HitmanPatcher.Core/Settings.cs
similarity index 82%
rename from patcher/Settings.cs
rename to patcher/HitmanPatcher.Core/Settings.cs
index eb7cabece..1153029bd 100644
--- a/patcher/Settings.cs
+++ b/patcher/HitmanPatcher.Core/Settings.cs
@@ -1,7 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-
namespace HitmanPatcher
{
public class Settings
@@ -30,20 +26,21 @@ public Settings()
trayDomains = new List();
}
- private static string GetSavePath()
+ public static string GetSavePath()
{
- if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\PeacockProject"))
+#if LINUX
+ return "peacock_patcher.conf";
+#else
+ string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ string folder = Path.Combine(appData, "PeacockProject");
+
+ if (!Directory.Exists(folder))
{
- Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\PeacockProject");
+ Directory.CreateDirectory(folder);
}
- string appData = Environment.GetFolderPath(Environment
- .SpecialFolder
- .ApplicationData);
-
- string folder = $@"{appData}\PeacockProject\";
- string config1 = folder + "peacock_patcher.conf";
- string config2 = folder + "peacock_patcher2.conf";
+ string config1 = Path.Combine(folder, "peacock_patcher.conf");
+ string config2 = Path.Combine(folder, "peacock_patcher2.conf");
if (File.Exists(config1))
{
@@ -51,6 +48,7 @@ private static string GetSavePath()
}
return config2;
+#endif
}
public void SaveToFile()
@@ -59,7 +57,7 @@ public void SaveToFile()
lines.Add(string.Format("CustomConfigDomain={0}", patchOptions.CustomConfigDomain));
lines.Add(string.Format("UseHttp={0}", patchOptions.UseHttp));
lines.Add(string.Format("DisableForceDynamicResources={0}", patchOptions.DisableForceOfflineOnFailedDynamicResources));
- lines.Add(string.Format("DarkModeEnabled={0}", darkModeEnabled));
+ lines.Add(string.Format("DarkModeEnabled={0}", darkModeEnabled));
lines.Add(string.Format("startInTray={0}", startInTray));
lines.Add(string.Format("minToTray={0}", minimizeToTray));
@@ -100,9 +98,9 @@ public static Settings GetFromFile()
case "DisableForceDynamicResources":
result.patchOptions.DisableForceOfflineOnFailedDynamicResources = bool.Parse(linecontents[1]);
break;
- case "DarkModeEnabled":
- result.darkModeEnabled = bool.Parse(linecontents[1]);
- break;
+ case "DarkModeEnabled":
+ result.darkModeEnabled = bool.Parse(linecontents[1]);
+ break;
case "startInTray":
result.startInTray = bool.Parse(linecontents[1]);
break;
diff --git a/patcher/HitmanPatcher.UI/App.config b/patcher/HitmanPatcher.UI/App.config
new file mode 100644
index 000000000..6abd8f17b
--- /dev/null
+++ b/patcher/HitmanPatcher.UI/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/patcher/HitmanPatcher.UI/HitmanPatcher.UI.csproj b/patcher/HitmanPatcher.UI/HitmanPatcher.UI.csproj
new file mode 100644
index 000000000..627e872f4
--- /dev/null
+++ b/patcher/HitmanPatcher.UI/HitmanPatcher.UI.csproj
@@ -0,0 +1,65 @@
+
+
+ WinExe
+ net8.0-windows
+ net46
+ enable
+ disable
+
+ HitmanPatcher
+ PeacockPatcher.UI
+
+ Debug;Release
+
+ Peacock Patcher
+ Peacock's HITMAN™ World of Assassination trilogy game patcher.
+ The Peacock Project
+ Peacock Patcher
+ Copyright © 2020-2024 grappigegovert & The Peacock Project
+ true
+ true
+ DpiUnawareGdiScaled
+ patcher.ico
+ 8.0.0.0
+ 8.0.0.0
+
+ true
+ $(DefineConstants);LEGACY
+ latest
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ Form
+
+
+ Form
+
+
+
diff --git a/patcher/HitmanPatcher.UI/ILRepack.targets b/patcher/HitmanPatcher.UI/ILRepack.targets
new file mode 100644
index 000000000..48ca8dd77
--- /dev/null
+++ b/patcher/HitmanPatcher.UI/ILRepack.targets
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/patcher/MainForm.Designer.cs b/patcher/HitmanPatcher.UI/MainForm.Designer.cs
similarity index 97%
rename from patcher/MainForm.Designer.cs
rename to patcher/HitmanPatcher.UI/MainForm.Designer.cs
index 5778da907..026a930e4 100644
--- a/patcher/MainForm.Designer.cs
+++ b/patcher/HitmanPatcher.UI/MainForm.Designer.cs
@@ -1,4 +1,4 @@
-namespace HitmanPatcher
+namespace HitmanPatcher
{
partial class MainForm
{
@@ -209,8 +209,8 @@ private void InitializeComponent()
this.novikovPictureBox.AccessibleDescription = "Icon showing the patcher status.";
this.novikovPictureBox.AccessibleName = "Status icon";
this.novikovPictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.novikovPictureBox.Image = global::HitmanPatcher.Properties.Resources.Novikov_standard;
- this.novikovPictureBox.InitialImage = ((System.Drawing.Image)(resources.GetObject("novikovPictureBox.InitialImage")));
+ this.novikovPictureBox.Image = Resources.Novikov_standard;
+ this.novikovPictureBox.InitialImage = Resources.Novikov_standard;
this.novikovPictureBox.Location = new System.Drawing.Point(313, 44);
this.novikovPictureBox.Margin = new System.Windows.Forms.Padding(4);
this.novikovPictureBox.Name = "novikovPictureBox";
@@ -222,7 +222,7 @@ private void InitializeComponent()
// trayIcon
//
this.trayIcon.ContextMenuStrip = this.trayMenu;
- this.trayIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("trayIcon.Icon")));
+ this.trayIcon.Icon = Resources.Icon;
this.trayIcon.Text = "Peacock Patcher";
this.trayIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.menuItemOpen_Click);
//
@@ -284,7 +284,7 @@ private void InitializeComponent()
this.Controls.Add(this.logLabel);
this.Controls.Add(this.logListView);
this.Controls.Add(this.serverAddressLabel);
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Icon = Resources.Icon;
this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
this.MaximizeBox = false;
this.MinimumSize = new System.Drawing.Size(423, 238);
diff --git a/patcher/MainForm.cs b/patcher/HitmanPatcher.UI/MainForm.cs
similarity index 97%
rename from patcher/MainForm.cs
rename to patcher/HitmanPatcher.UI/MainForm.cs
index 5a30dfe0e..a16b8549e 100644
--- a/patcher/MainForm.cs
+++ b/patcher/HitmanPatcher.UI/MainForm.cs
@@ -6,6 +6,7 @@
using System.Drawing;
using System.IO;
using System.Text;
+using Timer = System.Windows.Forms.Timer;
namespace HitmanPatcher
{
@@ -206,15 +207,15 @@ private void SetStatusImage(int status)
{
if (status == 0)
{
- novikovPictureBox.Image = Properties.Resources.Novikov_standard;
+ novikovPictureBox.Image = Resources.Novikov_standard;
}
else if (status > 0)
{
- novikovPictureBox.Image = Properties.Resources.Novikov_success;
+ novikovPictureBox.Image = Resources.Novikov_success;
}
else
{
- novikovPictureBox.Image = Properties.Resources.Novikov_error;
+ novikovPictureBox.Image = Resources.Novikov_error;
}
}
diff --git a/patcher/MainForm.resx b/patcher/HitmanPatcher.UI/MainForm.resx
similarity index 100%
rename from patcher/MainForm.resx
rename to patcher/HitmanPatcher.UI/MainForm.resx
diff --git a/patcher/Novikov_error.png b/patcher/HitmanPatcher.UI/Novikov_error.png
similarity index 100%
rename from patcher/Novikov_error.png
rename to patcher/HitmanPatcher.UI/Novikov_error.png
diff --git a/patcher/Novikov_standard.png b/patcher/HitmanPatcher.UI/Novikov_standard.png
similarity index 100%
rename from patcher/Novikov_standard.png
rename to patcher/HitmanPatcher.UI/Novikov_standard.png
diff --git a/patcher/Novikov_success.png b/patcher/HitmanPatcher.UI/Novikov_success.png
similarity index 100%
rename from patcher/Novikov_success.png
rename to patcher/HitmanPatcher.UI/Novikov_success.png
diff --git a/patcher/OptionsForm.Designer.cs b/patcher/HitmanPatcher.UI/OptionsForm.Designer.cs
similarity index 100%
rename from patcher/OptionsForm.Designer.cs
rename to patcher/HitmanPatcher.UI/OptionsForm.Designer.cs
diff --git a/patcher/OptionsForm.cs b/patcher/HitmanPatcher.UI/OptionsForm.cs
similarity index 100%
rename from patcher/OptionsForm.cs
rename to patcher/HitmanPatcher.UI/OptionsForm.cs
diff --git a/patcher/OptionsForm.resx b/patcher/HitmanPatcher.UI/OptionsForm.resx
similarity index 100%
rename from patcher/OptionsForm.resx
rename to patcher/HitmanPatcher.UI/OptionsForm.resx
diff --git a/patcher/HitmanPatcher.UI/Program.cs b/patcher/HitmanPatcher.UI/Program.cs
new file mode 100644
index 000000000..98b04f32f
--- /dev/null
+++ b/patcher/HitmanPatcher.UI/Program.cs
@@ -0,0 +1,25 @@
+namespace HitmanPatcher
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ private static void Main(string[] args)
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+
+#if !LEGACY
+ Application.SetHighDpiMode(HighDpiMode.SystemAware);
+#endif
+
+ var mainForm = MainForm.GetInstance();
+
+ Compositions.Logger = mainForm;
+
+ Application.Run(mainForm);
+ }
+ }
+}
diff --git a/patcher/HitmanPatcher.UI/Resources.cs b/patcher/HitmanPatcher.UI/Resources.cs
new file mode 100644
index 000000000..4d7bb9f53
--- /dev/null
+++ b/patcher/HitmanPatcher.UI/Resources.cs
@@ -0,0 +1,31 @@
+using System.Reflection;
+
+namespace HitmanPatcher
+{
+ public static class Resources
+ {
+ public static Icon Icon { get; }
+ public static Bitmap Novikov_standard { get; }
+ public static Bitmap Novikov_success { get; }
+ public static Bitmap Novikov_error { get; }
+
+ static Resources()
+ {
+ Icon = new Icon(ReadResource("patcher.ico"));
+ Novikov_standard = new Bitmap(ReadResource("Novikov_standard.png"));
+ Novikov_success = new Bitmap(ReadResource("Novikov_success.png"));
+ Novikov_error = new Bitmap(ReadResource("Novikov_error.png"));
+ }
+
+ private static Stream ReadResource(string name)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+
+ string resourcePath = assembly
+ .GetManifestResourceNames()
+ .Single(str => str.EndsWith(name));
+
+ return assembly.GetManifestResourceStream(resourcePath);
+ }
+ }
+}
diff --git a/patcher/TrayOptionsForm.Designer.cs b/patcher/HitmanPatcher.UI/TrayOptionsForm.Designer.cs
similarity index 100%
rename from patcher/TrayOptionsForm.Designer.cs
rename to patcher/HitmanPatcher.UI/TrayOptionsForm.Designer.cs
diff --git a/patcher/TrayOptionsForm.cs b/patcher/HitmanPatcher.UI/TrayOptionsForm.cs
similarity index 100%
rename from patcher/TrayOptionsForm.cs
rename to patcher/HitmanPatcher.UI/TrayOptionsForm.cs
diff --git a/patcher/TrayOptionsForm.resx b/patcher/HitmanPatcher.UI/TrayOptionsForm.resx
similarity index 100%
rename from patcher/TrayOptionsForm.resx
rename to patcher/HitmanPatcher.UI/TrayOptionsForm.resx
diff --git a/patcher/HitmanPatcher.UI/patcher.ico b/patcher/HitmanPatcher.UI/patcher.ico
new file mode 100644
index 000000000..82463fcea
Binary files /dev/null and b/patcher/HitmanPatcher.UI/patcher.ico differ
diff --git a/patcher/HitmanPatcher.csproj b/patcher/HitmanPatcher.csproj
deleted file mode 100644
index c2914cd5e..000000000
--- a/patcher/HitmanPatcher.csproj
+++ /dev/null
@@ -1,177 +0,0 @@
-
-
-
-
-
-
-
- Debug
- x64
- {0487D44B-B3E1-4A6E-91E0-47016C391A20}
- WinExe
- Properties
- HitmanPatcher
- PeacockPatcher
- v4.6
- 512
-
- True
- False
- False
- False
- False
- None.None.None.Increment
- None.None.None.Increment
- 8
-
-
-
- true
- bin\x64\Debug\
- DEBUG;TRACE;PLATFORM_STEAM;PLATFORM_EPIC;PLATFORM_GOG;PLATFORM_SCARLETT
- full
- x64
- prompt
- MinimumRecommendedRules.ruleset
- true
-
-
-
- bin\x64\Release\
- TRACE;PLATFORM_STEAM;PLATFORM_EPIC
- true
- pdbonly
- x64
- prompt
- MinimumRecommendedRules.ruleset
- true
-
-
-
- patcher.ico
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- CliLocale.resx
-
-
- Form
-
-
- MainForm.cs
-
-
-
-
- Form
-
-
- OptionsForm.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Form
-
-
- TrayOptionsForm.cs
-
-
- ResXFileCodeGenerator
- CliLocale.Designer.cs
-
-
- MainForm.cs
- Designer
-
-
- OptionsForm.cs
- Designer
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
- Designer
-
-
- True
- Resources.resx
- True
-
-
- TrayOptionsForm.Designer.cs
-
-
-
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
- True
- Settings.settings
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/patcher/HitmanPatcher.sln b/patcher/HitmanPatcher.sln
index 1d06a1846..08c4dbd59 100644
--- a/patcher/HitmanPatcher.sln
+++ b/patcher/HitmanPatcher.sln
@@ -1,22 +1,49 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.40629.0
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34316.72
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HitmanPatcher", "HitmanPatcher.csproj", "{0487D44B-B3E1-4A6E-91E0-47016C391A20}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HitmanPatcher.Core", "HitmanPatcher.Core\HitmanPatcher.Core.csproj", "{70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HitmanPatcher.CLI", "HitmanPatcher.CLI\HitmanPatcher.CLI.csproj", "{799814F1-919B-4BCA-9348-4F7F5F429CB9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HitmanPatcher.UI", "HitmanPatcher.UI\HitmanPatcher.UI.csproj", "{50B900C8-B244-4AAA-9130-5C0A6093B22D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x64 = Debug|x64
- Release|x64 = Release|x64
+ Debug - Linux|Any CPU = Debug - Linux|Any CPU
+ Debug|Any CPU = Debug|Any CPU
+ Release - Linux|Any CPU = Release - Linux|Any CPU
+ Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {0487D44B-B3E1-4A6E-91E0-47016C391A20}.Debug|x64.ActiveCfg = Debug|x64
- {0487D44B-B3E1-4A6E-91E0-47016C391A20}.Debug|x64.Build.0 = Debug|x64
- {0487D44B-B3E1-4A6E-91E0-47016C391A20}.Release|x64.ActiveCfg = Release|x64
- {0487D44B-B3E1-4A6E-91E0-47016C391A20}.Release|x64.Build.0 = Release|x64
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Debug - Linux|Any CPU.ActiveCfg = Debug - Linux|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Debug - Linux|Any CPU.Build.0 = Debug - Linux|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Release - Linux|Any CPU.ActiveCfg = Release - Linux|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Release - Linux|Any CPU.Build.0 = Release - Linux|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70EA1386-6A9D-47C2-ADE8-2F32CDD9E946}.Release|Any CPU.Build.0 = Release|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Debug - Linux|Any CPU.ActiveCfg = Debug - Linux|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Debug - Linux|Any CPU.Build.0 = Debug - Linux|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Release - Linux|Any CPU.ActiveCfg = Release - Linux|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Release - Linux|Any CPU.Build.0 = Release - Linux|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {799814F1-919B-4BCA-9348-4F7F5F429CB9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {50B900C8-B244-4AAA-9130-5C0A6093B22D}.Debug - Linux|Any CPU.ActiveCfg = Debug|Any CPU
+ {50B900C8-B244-4AAA-9130-5C0A6093B22D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {50B900C8-B244-4AAA-9130-5C0A6093B22D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {50B900C8-B244-4AAA-9130-5C0A6093B22D}.Release - Linux|Any CPU.ActiveCfg = Release|Any CPU
+ {50B900C8-B244-4AAA-9130-5C0A6093B22D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {50B900C8-B244-4AAA-9130-5C0A6093B22D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E12BBAA6-1725-4F91-A0E6-824E4AC9328E}
+ EndGlobalSection
EndGlobal
diff --git a/patcher/HitmanPatcher.sln.DotSettings b/patcher/HitmanPatcher.sln.DotSettings
index 80734dfb0..0d108e53e 100644
--- a/patcher/HitmanPatcher.sln.DotSettings
+++ b/patcher/HitmanPatcher.sln.DotSettings
@@ -9,6 +9,9 @@
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
+ PE
True
True
- True
\ No newline at end of file
+ True
+ True
+ True
\ No newline at end of file
diff --git a/patcher/Pinvoke.cs b/patcher/Pinvoke.cs
deleted file mode 100644
index 1daecabbc..000000000
--- a/patcher/Pinvoke.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace HitmanPatcher
-{
- // from https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
- [Flags]
- public enum MemProtection : uint
- {
- PAGE_EXECUTE = 0x00000010,
- PAGE_EXECUTE_READ = 0x00000020,
- PAGE_EXECUTE_READWRITE = 0x00000040,
- PAGE_EXECUTE_WRITECOPY = 0x00000080,
- PAGE_NOACCESS = 0x00000001,
- PAGE_READONLY = 0x00000002,
- PAGE_READWRITE = 0x00000004,
- PAGE_WRITECOPY = 0x00000008,
- PAGE_GUARD = 0x00000100,
- PAGE_NOCACHE = 0x00000200,
- PAGE_WRITECOMBINE = 0x00000400
- }
-
- // from https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
- // I've only listed the ones that I use
- [Flags]
- public enum ProcessAccess : uint
- {
- PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process.
- PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
- PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
- PROCESS_VM_OPERATION = 0x0008 // Required to perform an operation on the address space of a process using VirtualProtectEx
- }
-
- // from https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
- public enum PROCESSINFOCLASS
- {
- ProcessBasicInformation = 0,
- ProcessDebugPort = 7,
- ProcessWow64Information = 26,
- ProcessImageFileName = 27,
- ProcessBreakOnTermination = 29,
- ProcessSubsystemInformation = 75
- }
-
- // from https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
- [StructLayout(LayoutKind.Sequential)]
- public struct PROCESS_BASIC_INFORMATION
- {
- public IntPtr Reserved1;
- public IntPtr PebBaseAddress;
- public IntPtr Reserved2_0;
- public IntPtr Reserved2_1;
- public IntPtr UniqueProcessId;
- public IntPtr Reserved3;
- }
-
- public static class Pinvoke
- {
- [DllImport("kernel32", SetLastError = true)]
- public static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess, bool bInheritHandle, int dwProcessId);
-
- [DllImport("kernel32", SetLastError = true)]
- public static extern bool CloseHandle(IntPtr hObject);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool WriteProcessMemory([In] IntPtr hProcess, [In] IntPtr address, [In, MarshalAs(UnmanagedType.LPArray)] byte[] buffer, [In] UIntPtr size, [Out] out UIntPtr byteswritten);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool ReadProcessMemory([In] IntPtr hProcess, [In] IntPtr address, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] buffer, [In] UIntPtr size, [Out] out UIntPtr numberOfBytesRead);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool VirtualProtectEx([In] IntPtr hProcess, [In] IntPtr lpAddress, [In] UIntPtr dwSize, [In] MemProtection flNewProtect, [Out] out MemProtection lpflOldProtect);
-
- [DllImport("ntdll.dll")]
- public static extern int NtQueryInformationProcess(IntPtr hProcess, PROCESSINFOCLASS processInformationClass, out PROCESS_BASIC_INFORMATION processInformation, uint processInformationLength, out uint returnLength);
-
- public static int GetProcessParentPid(Process process)
- {
- IntPtr hProcess = OpenProcess(
- ProcessAccess.PROCESS_VM_READ
- | ProcessAccess.PROCESS_QUERY_INFORMATION,
- false, process.Id);
-
- if (hProcess == IntPtr.Zero)
- {
- throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get a process handle.");
- }
-
- PROCESS_BASIC_INFORMATION PEB = new PROCESS_BASIC_INFORMATION();
-
- int result = NtQueryInformationProcess(hProcess,
- PROCESSINFOCLASS.ProcessBasicInformation, out PEB,
- (uint) Marshal.SizeOf(PEB), out _);
-
- CloseHandle(hProcess);
- if (result != 0)
- {
- throw new Win32Exception(result, "(NTSTATUS)"); // not a w32 status code, but an NTSTATUS
- }
-
- return PEB.Reserved3.ToInt32(); // undocumented, but should hold the parent PID
- }
- }
-}
diff --git a/patcher/Program.cs b/patcher/Program.cs
deleted file mode 100644
index 2dadfe587..000000000
--- a/patcher/Program.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Security.Principal;
-using System.Windows.Forms;
-
-namespace HitmanPatcher
-{
- static class Program
- {
- public static bool HasAdmin;
-
- ///
- /// The main entry point for the application.
- ///
- [STAThread]
- private static void Main(string[] args)
- {
- HasAdmin = CheckForAdmin();
-#if !DEBUG
- Cli.EnsureConsole(args);
-#endif
-
- Cli.CliOptions o = Cli.CliOptions.FromArguments(args);
-
- if (o.Headless)
- {
- // ReSharper disable once LocalizableElement
- Console.WriteLine(CliLocale.HeadlessBanner);
- MemoryPatcher.PatchAllProcesses(new Cli.ConsoleLogger(), new MemoryPatcher.Options
- {
- AlwaysSendAuthHeader = true,
- CustomConfigDomain = o.Domain,
- DisableCertPinning = true,
- DisableForceOfflineOnFailedDynamicResources = o.OptionalDynRes,
- SetCustomConfigDomain = true,
- UseHttp = o.UseHttp
- });
- }
- else
- {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
-
- Application.Run(MainForm.GetInstance());
- }
- }
-
- static bool CheckForAdmin()
- {
- WindowsIdentity identity = WindowsIdentity.GetCurrent();
- WindowsPrincipal principal = new WindowsPrincipal(identity);
- return principal.IsInRole(WindowsBuiltInRole.Administrator);
- }
-
- }
-}
diff --git a/patcher/Properties/AssemblyInfo.cs b/patcher/Properties/AssemblyInfo.cs
deleted file mode 100644
index 40916fa24..000000000
--- a/patcher/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Peacock Patcher")]
-[assembly: AssemblyDescription("Peacock's HITMAN™ World of Assassination trilogy game patcher.")]
-#if DEBUG
- [assembly: AssemblyConfiguration("Debug")]
-#else
- [assembly: AssemblyConfiguration("Release")]
-#endif
-[assembly: AssemblyCompany("The Peacock Project")]
-[assembly: AssemblyProduct("Peacock Patcher")]
-[assembly: AssemblyCopyright("Copyright © 2020-2024 grappigegovert & The Peacock Project")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("4f027fef-87be-47f7-a0c6-f3978dc707e3")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("8.0.0.0")]
-[assembly: AssemblyFileVersion("8.0.0.0")]
diff --git a/patcher/Properties/Resources.Designer.cs b/patcher/Properties/Resources.Designer.cs
deleted file mode 100644
index 9aeb8e631..000000000
--- a/patcher/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// Dieser Code wurde von einem Tool generiert.
-// Laufzeitversion:4.0.30319.42000
-//
-// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
-// der Code erneut generiert wird.
-//
-//------------------------------------------------------------------------------
-
-namespace HitmanPatcher.Properties {
- ///
- /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
- ///
- // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
- // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
- // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
- // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- ///
- /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HitmanPatcher.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
- /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
- ///
- internal static System.Drawing.Bitmap Novikov_error {
- get {
- object obj = ResourceManager.GetObject("Novikov_error", resourceCulture);
- return ((System.Drawing.Bitmap)(obj));
- }
- }
-
- ///
- /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
- ///
- internal static System.Drawing.Bitmap Novikov_standard {
- get {
- object obj = ResourceManager.GetObject("Novikov_standard", resourceCulture);
- return ((System.Drawing.Bitmap)(obj));
- }
- }
-
- ///
- /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
- ///
- internal static System.Drawing.Bitmap Novikov_success {
- get {
- object obj = ResourceManager.GetObject("Novikov_success", resourceCulture);
- return ((System.Drawing.Bitmap)(obj));
- }
- }
- }
-}
diff --git a/patcher/Properties/Resources.resx b/patcher/Properties/Resources.resx
deleted file mode 100644
index 8de4204db..000000000
--- a/patcher/Properties/Resources.resx
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
-
- ..\Novikov_standard.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
-
- ..\Novikov_error.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
-
- ..\Novikov_success.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
-
diff --git a/patcher/Properties/Settings.Designer.cs b/patcher/Properties/Settings.Designer.cs
deleted file mode 100644
index eeee566bd..000000000
--- a/patcher/Properties/Settings.Designer.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace HitmanPatcher.Properties {
-
-
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
- internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
-
- private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
- public static Settings Default {
- get {
- return defaultInstance;
- }
- }
- }
-}
diff --git a/patcher/Properties/Settings.settings b/patcher/Properties/Settings.settings
deleted file mode 100644
index 39645652a..000000000
--- a/patcher/Properties/Settings.settings
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/patcher/build.bat b/patcher/build.bat
new file mode 100644
index 000000000..9b85e21b7
--- /dev/null
+++ b/patcher/build.bat
@@ -0,0 +1,18 @@
+@echo off
+rem Windows - CLI
+dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -c Release -p:PublishSingleFile=True --no-self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Portable
+dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r win-x64 -c Release -f net8.0 -p:PublishTrimmed=True -p:PublishSingleFile=True --self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-x64
+
+rem Linux - CLI
+dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r linux-x64 -c "Release - Linux" -p:PublishSingleFile=True --no-self-contained -p DebugType=none -p:IsLinux=true -o Publish/Linux-Portable
+dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -r linux-x64 -c "Release - Linux" -f net8.0 -p:PublishTrimmed=True -p:PublishSingleFile=True --self-contained -p DebugType=none -p:IsLinux=true -o Publish/Linux-x64
+
+rem Windows - UI
+dotnet publish HitmanPatcher.UI/HitmanPatcher.UI.csproj -c Release -p:PublishSingleFile=True --no-self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Portable
+dotnet publish HitmanPatcher.UI/HitmanPatcher.UI.csproj -r win-x64 -c Release -p:PublishSingleFile=True --self-contained -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-x64
+
+rem Windows (.NET Framework)
+dotnet publish HitmanPatcher.CLI/HitmanPatcher.CLI.csproj -c Release -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Legacy -p:IsLegacy=True
+dotnet publish HitmanPatcher.UI/HitmanPatcher.UI.csproj -c Release -p DebugType=none -p:EnableWindowsTargeting=True -o Publish/Windows-Legacy -p:IsLegacy=True
+
+pause
\ No newline at end of file