From c88000c33022056ff0a75ee36d5cd86a1de83706 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Wed, 5 Jan 2022 01:28:13 -0800 Subject: [PATCH 001/276] build: add publishing profiles --- ...e_net6.0-windows_framework-dependent.pubxml | 17 +++++++++++++++++ .../Release_net6.0-windows_win-x64.pubxml | 18 ++++++++++++++++++ .../Release_net6.0-windows_win-x86.pubxml | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml create mode 100644 src/Properties/PublishProfiles/Release_net6.0-windows_win-x64.pubxml create mode 100644 src/Properties/PublishProfiles/Release_net6.0-windows_win-x86.pubxml diff --git a/src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml b/src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml new file mode 100644 index 00000000..efa692e9 --- /dev/null +++ b/src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml @@ -0,0 +1,17 @@ + + + + + Release + Any CPU + ..\bin\Release\net6.0-windows\publish\ + FileSystem + net6.0-windows + false + true + true + + + diff --git a/src/Properties/PublishProfiles/Release_net6.0-windows_win-x64.pubxml b/src/Properties/PublishProfiles/Release_net6.0-windows_win-x64.pubxml new file mode 100644 index 00000000..7dd060fb --- /dev/null +++ b/src/Properties/PublishProfiles/Release_net6.0-windows_win-x64.pubxml @@ -0,0 +1,18 @@ + + + + + Release + Any CPU + ..\bin\Release\net6.0-windows\win-x64\publish\ + FileSystem + net6.0-windows + true + true + true + win-x64 + False + + diff --git a/src/Properties/PublishProfiles/Release_net6.0-windows_win-x86.pubxml b/src/Properties/PublishProfiles/Release_net6.0-windows_win-x86.pubxml new file mode 100644 index 00000000..bf2b3263 --- /dev/null +++ b/src/Properties/PublishProfiles/Release_net6.0-windows_win-x86.pubxml @@ -0,0 +1,18 @@ + + + + + Release + Any CPU + ..\bin\Release\net6.0-windows\win-x64\publish\ + FileSystem + net6.0-windows + true + true + true + win-x86 + False + + From d83cb8bed886a022fef0df3e39419d3c179ef351 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:48:59 -0800 Subject: [PATCH 002/276] ci:(release): de-ref CSPROJ_RELPATH via workflow syntax instead of Posh sytax --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27e0819e..2c367850 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: # BUILD ################ - name: dotnet-publish - run: dotnet publish $env:CSPROJ_RELPATH -c Release --no-self-contained -t ${{ env.TFM }} --RID ${{ matrix.RID }} -p:ContinuousIntegrationBuild=true + run: dotnet publish ${{ env.CSPROJ_RELPATH }} -c Release -t ${{ env.TFM }} --RID ${{ matrix.RID }} -p:ContinuousIntegrationBuild=true # required variables: TargetFramework, RuntimeIdentifier, GitVersion_FullSemVer - name: Compress-PublishArtifacts From 8f62a87a31cb8346265098badd630364faf90db8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:47:05 -0800 Subject: [PATCH 003/276] ci(release): change RID to a matrix variable for win-x86, win-x64 --- .github/workflows/release.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1629de4b..27e0819e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,10 +18,13 @@ jobs: name: Build runs-on: windows-latest env: - RID: "win-x86" TFM: "net5.0-windows" ASSEMBLYNAME: "HXE" PROJPATH: "./src/HXE.csproj" + strategy: + fail-fast: false + matrix: + RID: [ "win-x86", "win-x64" ] steps: ################ @@ -53,7 +56,7 @@ jobs: # BUILD ################ - name: dotnet-publish - run: dotnet publish $env:CSPROJ_RELPATH -c Release --no-self-contained -p:ContinuousIntegrationBuild=true + run: dotnet publish $env:CSPROJ_RELPATH -c Release --no-self-contained -t ${{ env.TFM }} --RID ${{ matrix.RID }} -p:ContinuousIntegrationBuild=true # required variables: TargetFramework, RuntimeIdentifier, GitVersion_FullSemVer - name: Compress-PublishArtifacts @@ -77,7 +80,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: publish-artifacts - path: bin/Release/${{ env.TFM }}/${{ env.RID }}/publish + path: bin/Release/${{ env.TFM }}/${{ matrix.RID }}/publish - name: Publish to GitHub Packages working-directory: bin/Release From 2df4884cd30515f53ee2b7ecc54974ac13c1febe Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:49:45 -0800 Subject: [PATCH 004/276] ci(release): trim comments and empty lines for Dotnet block --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c367850..d8a48c98 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: # Authenticates packages to push to GPR - uses: actions/setup-dotnet@v3.0.3 with: - dotnet-version: "6.0.x" # SDK Version to use. + dotnet-version: "6.0.x" include-prerelease: true - name: Add GitHub Package Repository From 5df6b49f8738bc3522eee5a710aaaceaeaec98dc Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:50:24 -0800 Subject: [PATCH 005/276] ci(release): set Target Framework to net6.0-windows --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8a48c98..b8296e14 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: name: Build runs-on: windows-latest env: - TFM: "net5.0-windows" + TFM: "net6.0-windows" ASSEMBLYNAME: "HXE" PROJPATH: "./src/HXE.csproj" strategy: From b43b573631079b7ba2e26e8a12f34cb02015d7e1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:52:42 -0800 Subject: [PATCH 006/276] ci(release): change path of packaged Publish artifacts --- .github/workflows/release.yml | 4 ++-- .releaserc.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8296e14..5be04322 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,7 +64,7 @@ jobs: $publishPath = Resolve-Path ".\bin\Release\$env:TFM\$env:RID\publish"; $archiveName = "$env:ASSEMBLYNAME.$env:TFM-$env:RID.$env:GitVersion_FullSemVer.zip"; Set-Location $publishPath; - Compress-Archive -Path $publishPath -DestinationPath $archiveName -CompressionLevel Optimal; + Compress-Archive -Path $publishPath -DestinationPath ${{ github.workspace }}\bin\Release\publish\$archiveName -CompressionLevel Optimal; ################ # RELEASE @@ -80,7 +80,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: publish-artifacts - path: bin/Release/${{ env.TFM }}/${{ matrix.RID }}/publish + path: bin/Release/publish - name: Publish to GitHub Packages working-directory: bin/Release diff --git a/.releaserc.yaml b/.releaserc.yaml index 2217cbd9..ed0801b5 100644 --- a/.releaserc.yaml +++ b/.releaserc.yaml @@ -28,7 +28,7 @@ ['@semantic-release/github', { "assets": [ - {"path": "bin/release/win-x64/publish"} + {"path": "bin/release/publish"} ] } ] From cd2b42f2f32f0785953bad884777643cd4c4c30d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:57:04 -0800 Subject: [PATCH 007/276] build: IncludeNativeLibrariesForSelfExtract The .NET runtime depependency sni.dll will now be embedded in our app. Why is it a default dependency of win-x64? and win-x86? Why is it copied to the Publish folder if it's a prerequisite of the .NET runtime? --- src/HXE.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index d1fb5c2b..5d52d7ba 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -37,8 +37,7 @@ true - win-x86 - + true {ACAA5D9F-B23D-43E1-B2DF-8C03230975A1} Properties From 5a20b587e0c67601d4d7b9eea403bfce64771de4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:57:39 -0800 Subject: [PATCH 008/276] build: EnableCompressionInSingleFile --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index 5d52d7ba..41cf124c 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -37,6 +37,7 @@ true + true true {ACAA5D9F-B23D-43E1-B2DF-8C03230975A1} From a9345c76ce54d3f500fc029d2008e88b0ba11367 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:58:23 -0800 Subject: [PATCH 009/276] build: if undefined, set RuntimeIdentifier to win-x64 --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index 41cf124c..d018ea4f 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -36,6 +36,7 @@ + win-x64 true true true From 527e808bdd07c36ce01f47f0fcc563f86f8cd4f5 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 01:59:29 -0800 Subject: [PATCH 010/276] build: explicitly enable SelfContained --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index d018ea4f..395491f1 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -36,6 +36,7 @@ + true win-x64 true true From f289e814324297524ff9890ee7e0e3f43574884f Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 02:00:00 -0800 Subject: [PATCH 011/276] build: enable isTrimmable --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 395491f1..04f50bd4 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -34,7 +34,7 @@ https://github.com/HaloSPV3/HXE snupkg - + true true win-x64 From c97821bc925c338aaa30ccad3b3d986427596f19 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 02:10:01 -0800 Subject: [PATCH 012/276] fix: replace WMI for filesystem compression with DotNet functionality I hope this works the way I think it does. I'm worried the attribute will be set without the file being compressed. --- src/HXE.csproj | 1 - src/Installer.cs | 21 +++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 04f50bd4..825c05c9 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -119,7 +119,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - diff --git a/src/Installer.cs b/src/Installer.cs index 52a1447c..651ed81d 100644 --- a/src/Installer.cs +++ b/src/Installer.cs @@ -22,7 +22,6 @@ using System; using System.IO; using System.IO.Compression; -using System.Management; using System.Threading; using System.Threading.Tasks; using HXE.Properties; @@ -70,18 +69,20 @@ public static void Install(string source, string target, IProgress progr if (!Directory.Exists(target)) Directory.CreateDirectory(target); - if (enableLZNT1) + if (enableLZNT1) /// TODO: refactor to new Method for use from other Classes. { - var dirInfo = new DirectoryInfo(target); - if ((dirInfo.Attributes & FileAttributes.Compressed) != FileAttributes.Compressed) + string[] directories = Directory.GetDirectories(target); + string[] files = Directory.GetFiles(target); + foreach (string directoryPath in directories) { - var objPath = $"Win32_Directory.Name='{target}'"; - using (var dir = new ManagementObject(objPath)) - { - var outParams = dir.InvokeMethod("Compress", null, null); - uint ret = (uint) outParams.Properties["ReturnValue"].Value; - } + new DirectoryInfo(directoryPath).Attributes |= FileAttributes.Compressed; } + foreach (string filePath in files) + { + new FileInfo(filePath).Attributes |= FileAttributes.Compressed; + } + + /// TODO: Introduce and display progress of changes using Events } Info("Gracefully created target directory"); From 730191a4c7a6adf239e9fdd19ad9a011b8e19052 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 02:27:04 -0800 Subject: [PATCH 013/276] chore(vscode): add markdown rules --- .vscode/settings.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2e4b7d93..5a3b6411 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,11 @@ "conv-pr", // Conventional Pull Request config "commitizen", "pull-requests" // pull requests workflow - ] + ], + "markdownlint.config": { + "MD028": false, + "MD025": { + "front_matter_title": "" + } + } } From 6c8e4b7510d29a80cc0fb45c14ff212ea298e95f Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 9 Jan 2022 02:29:13 -0800 Subject: [PATCH 014/276] docs(readme): add Requirements --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 71927e45..6ca78ee6 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,30 @@ The USAGE document goes into detail on how to use HXE. In a nutshell: # configure the kernel .\hxe.exe -config + +# Requirements + +## Operating System + +| Minimum | Recommended +| ---------------------------------------- | ----------- +| Windows 7 SP1 32-bit (w/ addl. software) | Windows 10 64-bit + +## .NET 6.0 + +Because HXE is built on the relatively new .NET 6, you may need to download the [.NET 6.0 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) for this app to work. Hopefully, this will be distributed via Windows Updates to Windows 10 and Windows 11 sooner rather than later. +For 64-bit PCs: [Dotnet Runtime (Desktop) 6.0.1 Windows x64 Installer](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.1-windows-x64-installer) +For 32-bit PCs: [Dotnet Runtime (Desktop) 6.0.1 Windows x86 Installer](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.1-windows-x86-installer) + +### Windows 7/8.1 + +Additional software dependencies to be installed for this .NET-based app to work on Windows 8.1 and Windows 7 +Read the [Microsoft docs](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net60#additional-deps) to learn what you need and how to get it. + +## Note: Upgrading To Windows 10 + +Using the [Windows Installation Media Creation Tool](https://www.microsoft.com/en-us/software-download/windows10?36261b60-2f68-4336-abe2-4b00f210b6aa=True), you can still upgrade to Windows 10 with your Windows 7/8/8.1 license. +HOWEVER... + +- If your hardware distributor does not make Windows 10 drivers for your hardware, you may have a worse Windows 10 experience than expected. +- Some drivers made for earlier Windows releases may work on Windows 10; some won't. You won't know until you try. From 63433907f582b6a0fa18fc92a59789af2d2ff99d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 14 Feb 2022 02:57:05 -0800 Subject: [PATCH 015/276] feat: Add CLI alternative to Positions GUI --- src/CLI/Positions.cs | 157 +++++++++++++++++++++++++++++++++++++++++++ src/Program.cs | 3 + 2 files changed, 160 insertions(+) create mode 100644 src/CLI/Positions.cs diff --git a/src/CLI/Positions.cs b/src/CLI/Positions.cs new file mode 100644 index 00000000..00f9848c --- /dev/null +++ b/src/CLI/Positions.cs @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2022 Noah Sherwin + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +using System; +using System.IO; + +namespace HXE.CLI +{ + public static class Positions + { + public static void Run(string source = null, string target = null) + { + Console.Info("Read the file \"OS_Settings.User.xml\" and write its weapons positions to a .bin file."); + FileInfo fiSource = null; + FileInfo fiTarget = null; + + while (fiSource == null) + { + try + { + fiSource = GetSource(); + } + catch (Exception e) + { + Console.Error(e.ToString()); + } + } + + while (fiTarget == null) + { + try + { + fiTarget = GetTarget(); + } + catch (Exception e) + { + Console.Error(e.ToString()); + } + } + + try + { + Save(fiSource, fiTarget); + } + catch (Exception e) + { + Console.Error(e.ToString()); + } + + + } + + private static FileInfo GetSource(string source = null) + { + FileInfo fileInfo = null; + + Console.Info("Full path of OS_Settings.User.xml:"); + + if (source == null) + { + string input = System.Console.In.ReadLine(); + + if (string.IsNullOrWhiteSpace(input)) + { + throw new NullReferenceException("The supplied path was null, empty, or whitespace."); + } + + fileInfo = new FileInfo(Path.GetFullPath(input)); + } + else + { + Console.Info(source); + fileInfo = new FileInfo(Path.GetFullPath(source)); + } + + if (!fileInfo.Exists) + { + throw new FileNotFoundException($"The file {fileInfo.Name} was not found."); + } + + if (fileInfo.Name != "OS_Settings.User.xml") + { + throw new ArgumentException("The provided file is not OS_Settings.User.xml."); + } + + return fileInfo; + } + + private static FileInfo GetTarget(string target = null) + { + FileInfo fileInfo; + + Console.Info("Full path of target/output .bin file:"); + + if (target == null) + { + string input = System.Console.In.ReadLine(); + + if (string.IsNullOrWhiteSpace(input)) + { + throw new NullReferenceException("The supplied path was null, empty, or whitespace."); + } + + fileInfo = new FileInfo(Path.GetFullPath(input)); + } + else + { + Console.Info(target); + fileInfo = new FileInfo(Path.GetFullPath(target)); + } + + if (!fileInfo.Exists) + { + throw new FileNotFoundException($"The file {fileInfo.Name} was not found."); + } + + if (!fileInfo.Extension.EndsWith("bin")) + { + throw new ArgumentException("The provided file lacks the .bin extension."); + } + + return fileInfo; + + } + + private static void Save(FileInfo source, FileInfo target) + { + Console.Info("Saving weapon positions..."); + + var openSauce = (OpenSauce) source.FullName; + + openSauce.Load(); + openSauce.Objects.Weapon.Save(target.FullName); + openSauce.Objects.Weapon.Load(target.FullName); + + foreach (var position in openSauce.Objects.Weapon.Positions) + Console.Debug($"Weapon: {position.Name} | I/J/K: {position.Position.I}/{position.Position.J}/{position.Position.K}"); + } + } +} diff --git a/src/Program.cs b/src/Program.cs index 7fa167ae..cbeef879 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -160,6 +160,9 @@ private static void InvokeProgram(string[] args) app = new Application(); _ = app.Run(test_positions); app.Shutdown(); + //string target = Path.Combine(CurrentDirectory, "positions.bin"); + //Positions.Run(source, target); + Logs("TODO: Positions test requires an OpenSauce.User.xml file."); Logs("Positions Test: Succeeded"); } catch (Exception e) From 6cff163f6ff11ece6569559045520241d0af4e3a Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 14 Feb 2022 02:37:46 -0800 Subject: [PATCH 016/276] fix: support targeting NETFX 4.6.2, 4.8.0 Some faimilar features are not available in .NET Framework. NOT SUPPORTED IN NET4: - string.Split(char[] separator, StringSplitOptions options) - string.Split(string separator) - System.Net.Http.HttpResponseMessage.Content.ReadAsStream() --- src/HXE.csproj | 8 ++++++-- src/MCC/Halo1.cs | 2 +- src/Program.cs | 2 +- src/Steam/Libraries.cs | 2 +- src/Update.cs | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 825c05c9..f840a5a6 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -6,8 +6,8 @@ AnyCPU false Exe - net5.0-windows - net5.0-windows;net6.0-windows + net462 + net462;net480;net5.0-windows;net6.0-windows HXE.Program HXE HXE @@ -120,6 +120,10 @@ all + + + + diff --git a/src/MCC/Halo1.cs b/src/MCC/Halo1.cs index 12dc24e3..9f7e264b 100644 --- a/src/MCC/Halo1.cs +++ b/src/MCC/Halo1.cs @@ -114,7 +114,7 @@ public static bool Halo1DLLIsCertified() try { var response = Client.GetAsync(uri).Result; - MemoryStream ms = (MemoryStream) response.Content.ReadAsStream(); + var ms = (MemoryStream) response.Content.ReadAsStreamAsync().Result; byte[] msArray = ms.ToArray(); remoteCert = new X509Certificate(msArray); } diff --git a/src/Program.cs b/src/Program.cs index cbeef879..00441e0e 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -372,7 +372,7 @@ internal static int GetLongestStringLength(string[] strings) { foreach (string line in s.Split('\n')) { - lines.AddRange(line.Split('\r', StringSplitOptions.RemoveEmptyEntries)); + lines.AddRange(line.Split('\r').Where(l => !string.IsNullOrWhiteSpace(l))); } } diff --git a/src/Steam/Libraries.cs b/src/Steam/Libraries.cs index c905b794..f4bb7f04 100644 --- a/src/Steam/Libraries.cs +++ b/src/Steam/Libraries.cs @@ -117,7 +117,7 @@ public static void ParseLibrary(File libraryFoldersVdf = null) { string text = libraryFoldersVdf.ReadAllText(); - List libs = text.Split("\n").ToList(); /// Start by adding each line to a list. + List libs = text.Split('\n').ToList(); /// Start by adding each line to a list. libs = libs.Where(line => line.Contains("\"path\"")).ToList(); /// Filter the list for entries containing `"path"`. foreach (string line in libs) diff --git a/src/Update.cs b/src/Update.cs index c703a5ea..293c7e2e 100644 --- a/src/Update.cs +++ b/src/Update.cs @@ -78,7 +78,7 @@ public async void Import(string uri) Info("Inferred web request manifest - " + uri); using (var rm = await Client.GetAsync(uri)) - using (var sr = new StreamReader(rm.Content.ReadAsStream() ?? throw new NullReferenceException("No response."))) + using (var sr = new StreamReader(await rm.Content.ReadAsStreamAsync() ?? throw new NullReferenceException("No response."))) { data = sr.ReadToEnd(); } From fbae24fcfe0b1cbcaaac13d55b4c0f586be7e9f7 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:21:21 -0800 Subject: [PATCH 017/276] fix: remove/replace WinForms references Use Microsoft.Win32 or WPF equivalents instead. --- src/HCE/Executable.cs | 18 +++++++++--------- src/HXE.csproj | 1 - src/Kernel.cs | 16 +++++++++------- src/Positions.xaml.cs | 21 +++++++++++---------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/HCE/Executable.cs b/src/HCE/Executable.cs index f897d812..a4958f74 100644 --- a/src/HCE/Executable.cs +++ b/src/HCE/Executable.cs @@ -66,16 +66,16 @@ public static Executable Detect() var log = (File) Paths.Exception; log.AppendAllText("The inferred executable path was probably malformed or incomplete.\n Error: " + e + "\n"); - using (System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog()) + var ofd = new Microsoft.Win32.OpenFileDialog { - ofd.InitialDirectory = GetFolderPath(SpecialFolder.Desktop); - ofd.Filter = "Halo Custom Edition (haloce.exe)|haloce.exe|Halo Retail/Trial (halo.exe)|halo.exe"; - ofd.FilterIndex = 1; - ofd.RestoreDirectory = true; - - if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) - fullName = GetFullPath(ofd.FileName); - } + InitialDirectory = GetFolderPath(SpecialFolder.Desktop), + Filter = "Halo Custom Edition (haloce.exe)|haloce.exe|Halo Retail/Trial (halo.exe)|halo.exe", + FilterIndex = 1, + RestoreDirectory = true + }; + + if (ofd.ShowDialog() == true) + fullName = GetFullPath(ofd.FileName); } if (System.IO.File.Exists(fullName)) diff --git a/src/HXE.csproj b/src/HXE.csproj index f840a5a6..73ad6918 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -49,7 +49,6 @@ true hxe true - true x64 diff --git a/src/Kernel.cs b/src/Kernel.cs index d4b18744..08e1aebf 100644 --- a/src/Kernel.cs +++ b/src/Kernel.cs @@ -31,7 +31,6 @@ using static System.IO.Directory; using static System.IO.Path; using static System.Text.Encoding; -using static System.Windows.Forms.Screen; using static HXE.Console; using static HXE.HCE.Profile.ProfileAudio; using static HXE.HCE.Profile.ProfileVideo; @@ -436,19 +435,22 @@ void Video() { if (!configuration.Video.ResolutionEnabled) { + var w = System.Windows.SystemParameters.PrimaryScreenWidth; + var h = System.Windows.SystemParameters.PrimaryScreenHeight; + // infer from resolution if Native Resoluton preferred. if (executable.Video.Width == 0 || executable.Video.Height == 0) { - executable.Video.Width = (ushort) PrimaryScreen.Bounds.Width; - executable.Video.Height = (ushort) PrimaryScreen.Bounds.Height; + executable.Video.Width = (ushort) System.Windows.SystemParameters.PrimaryScreenWidth; + executable.Video.Height = (ushort) System.Windows.SystemParameters.PrimaryScreenHeight; Core("BLAM.VIDEO.RESOLUTION: No resolution provided. Applied native resolution to executable."); } - else if (executable.Video.Width > (ushort) PrimaryScreen.Bounds.Width || - executable.Video.Height > (ushort) PrimaryScreen.Bounds.Height) + else if (executable.Video.Width > (ushort) System.Windows.SystemParameters.PrimaryScreenWidth || + executable.Video.Height > (ushort) System.Windows.SystemParameters.PrimaryScreenHeight) { - executable.Video.Width = (ushort) PrimaryScreen.Bounds.Width; - executable.Video.Height = (ushort) PrimaryScreen.Bounds.Height; + executable.Video.Width = (ushort) System.Windows.SystemParameters.PrimaryScreenWidth; + executable.Video.Height = (ushort) System.Windows.SystemParameters.PrimaryScreenHeight; Core("BLAM.VIDEO.RESOLUTION: Resolution out of bounds. Applied native resolution to executable."); } diff --git a/src/Positions.xaml.cs b/src/Positions.xaml.cs index 47175737..d5a901f1 100644 --- a/src/Positions.xaml.cs +++ b/src/Positions.xaml.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * * This software is provided 'as-is', without any express or implied @@ -19,7 +19,7 @@ */ using System.Windows; -using System.Windows.Forms; +using Microsoft.Win32; using static HXE.Console; using MessageBox = System.Windows.MessageBox; @@ -28,7 +28,7 @@ namespace HXE /// /// Interaction logic for Positions.xaml /// - public partial class Positions + public partial class Positions : Window { private string _source; private string _target; @@ -67,16 +67,17 @@ private void Cancel(object sender, RoutedEventArgs e) private void BrowseSource(object sender, RoutedEventArgs e) { - using (var dialog = new OpenFileDialog()) - { - dialog.DefaultExt = ".xml"; - dialog.Filter = "XML files (*.xml)|*.xml"; + var dialog = new OpenFileDialog + { + DefaultExt = ".xml", + Filter = "XML files (*.xml)|*.xml" + }; - if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; + if (dialog.ShowDialog() != true) return; _source = dialog.FileName; SourceTextBox.Text = _source; - } + } private void BrowseTarget(object sender, RoutedEventArgs e) @@ -87,7 +88,7 @@ private void BrowseTarget(object sender, RoutedEventArgs e) Filter = "BIN files (*.bin)|*.bin" }; - if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; + if (dialog.ShowDialog() != true) return; _target = dialog.FileName; TargetTextBox.Text = _target; From 66addaccafd65b2a156fe5ca4fc598a24814aca0 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:25:52 -0800 Subject: [PATCH 018/276] build: simplify GitVersion prebuild script --- src/.msb.prebuild.ps1 | 53 ++++++++----------------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/src/.msb.prebuild.ps1 b/src/.msb.prebuild.ps1 index 0f9e5f06..88434e36 100644 --- a/src/.msb.prebuild.ps1 +++ b/src/.msb.prebuild.ps1 @@ -8,59 +8,26 @@ function prebuild { - $minVer_isShallow = [version]'2.15.0.0'; - $minVer_unshallow = [version]'2.1.4.0'; - $gitBinVer = ""; $isShallow = $true; - # 0. Announce - Write-Host "0. GitVersion cannot determine the next version in shallow reposistories.`n", - "`tWe will use Git to determine if the current repository needs to be un-shallowed.`n", - "Checking if Git is available..."; + # Announce + Write-Host "GitVersion requires unshallow repositories.`n", + "We will use Git to determine if the current repository needs to be un-shallowed."; - # 1. Ensure Git is available - try - { - Write-Host "1. Git was found.`n", - "It is $(git --version) at...`n", - (Get-Command -Name git).path - - $gitBinVer = [version]('{2}.{3}.{4}.{6}' -f (git --version).split(' ').split('.')) - } - catch - { - Write-Error "Git is not installed or it is not in PATH!"; - throw - } - - # 2. Check if the repository is shallow - Write-Host "2. Checking if repository is shallow..." - if ($gitBinVer -gt $minVer_isShallow) # GitVersion >= 2.15.0.0 - { - $isShallow = git rev-parse --is-shallow-repository - } - else - { - Write-Debug "Git Version less than 2.15.0.0" - $isShallow = Test-Path (Join-Path $GitStatus.GitDir shallow) - } + # Check if the repository is shallow + Write-Host "Checking if repository is shallow..." + $isShallow = git rev-parse --is-shallow-repository - # 3. If the repository is shallow, then unshallow + # If the repository is shallow, then unshallow if ($isShallow -eq $true) { - Write-Host "3. Repository is shallow. Fetching full history..." - if ($gitBinVer -lt $minVer_unshallow) # GitVersion < 2.1.4.0 (exact version unknown) - { - git fetch --depth=0; - } - else { - git fetch --unshallow - } + Write-Warning "Repository is shallow. Fetching full history..." + git fetch --unshallow Write-Host "Fetch Completed. Proceeding to Build...`n" } else { - Write-Host "3. Repository is complete. Proceeding to Build..." + Write-Host "Repository is complete. Proceeding to Build..." } } From ae905b96d822e0d353e267c96f3f9db691e61af7 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:29:07 -0800 Subject: [PATCH 019/276] style: add end-of-file newline style: set charset to UTF-8 BOM --- src/HCE/Executable.cs | 4 ++-- src/Kernel.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/HCE/Executable.cs b/src/HCE/Executable.cs index a4958f74..eb1902fa 100644 --- a/src/HCE/Executable.cs +++ b/src/HCE/Executable.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * Copyright (c) 2021 Noah Sherwin * @@ -238,4 +238,4 @@ public class MiscellaneousOptions public bool NoVideo { get; set; } } } -} \ No newline at end of file +} diff --git a/src/Kernel.cs b/src/Kernel.cs index 08e1aebf..d8b4ddc7 100644 --- a/src/Kernel.cs +++ b/src/Kernel.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * Copyright (c) 2021 Noah Sherwin * @@ -1078,7 +1078,7 @@ public class ConfigurationMain public class ConfigurationVideo { - public bool ResolutionEnabled { get; set; } = false; /* custom resolution */ + public bool ResolutionEnabled { get; set; } = false; /* auto resolution */ public bool Uncap { get; set; } = true; /* unlock framerate */ public bool Quality { get; set; } /* set to false by default for optimisation */ public bool GammaOn { get; set; } = false; /* enable hce gamma */ @@ -1109,4 +1109,4 @@ public class ConfigurationTweaks } } } -} \ No newline at end of file +} From 258c0ec4782bf42c8128c76ecf2c2113c68f093a Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:36:40 -0800 Subject: [PATCH 020/276] build: change CPU arch conditions from Equals to Contains --- src/HXE.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 73ad6918..b453476e 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -50,11 +50,11 @@ hxe true - + x64 false - + x86 true From a35c181a29bf2642003e7e96cd0a196ed5d8c5b9 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:38:53 -0800 Subject: [PATCH 021/276] build: set satellite resource langs to en-us --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index b453476e..c524009a 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -49,6 +49,7 @@ true hxe true + en-US x64 From 809e5be56722acf114210fb374c91e0caf5e722d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:39:24 -0800 Subject: [PATCH 022/276] build: enable InvariantGlobalization --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index c524009a..f9208e64 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -50,6 +50,7 @@ hxe true en-US + true x64 From 9b0e09de05f3e2e08e54c3337a27745a0b77063f Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:41:22 -0800 Subject: [PATCH 023/276] build: set custom var `Win7SF` if SDK version is 6.0.300 or greater The Windows 7 SingleFile host will be introduced in .NET 6.0.300. --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index f9208e64..92ba1e97 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -51,6 +51,7 @@ true en-US true + $([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '6.0.300')) x64 From 53537816daf7fb10f4056e7c1b51e5fdde132b7b Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:44:21 -0800 Subject: [PATCH 024/276] build: only allow PublishSingleFile with Win7 if Win7SF is True --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index 92ba1e97..f65c33a7 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -52,6 +52,7 @@ en-US true $([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '6.0.300')) + $(Win7SF) x64 From 1b90854110ee7504d61c02d14612611a6cd32f7c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:46:18 -0800 Subject: [PATCH 025/276] build: do not publish Release symbols --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index f65c33a7..28554a16 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -66,6 +66,7 @@ DEBUG;TRACE + false TRACE From a8863a6be87100ca62841c73cda542303f3612e8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 18:59:13 -0800 Subject: [PATCH 026/276] build: move publish-related props and conditions - if both PublishSingleFile and RuntimeIdentifier are unset, disable PublishSingleFile - if RuntimeIdentifier is unset and PublishSingleFile is enabled, set RuntimeIdentifier to 'win-x86' - if SelfContained is unset, but RuntimeIdentifier *is*, enable SelfContained - if PublishSingleFile is enabled.. ...enable SelfContained ...enable IncludeNativeLibrariesForSelfExtract ...enable EnableCompressionInSingleFile --- src/HXE.csproj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 28554a16..e58356d5 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -36,11 +36,9 @@ true - true - win-x64 - true - true - true + false + win-x86 + true {ACAA5D9F-B23D-43E1-B2DF-8C03230975A1} Properties @@ -69,6 +67,11 @@ false TRACE + + true + true + true + .root\%(Filename)%(Extension) From 3447c8c669ec35d82d8808ac9258df9e4651c766 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:01:04 -0800 Subject: [PATCH 027/276] style: fix indentation --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index e58356d5..bc00ded4 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -78,7 +78,7 @@ Never - .root\.github\%(RecursiveDir)%(Filename)%(Extension) + .root\.github\%(RecursiveDir)%(Filename)%(Extension) .docs\%(RecursiveDir)%(Filename)%(Extension) From e7e5b006408f4b69b83d86ba9cf979bd31e5ff6b Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:07:01 -0800 Subject: [PATCH 028/276] style: set charset to UTF-8 BOM --- src/Positions.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Positions.xaml.cs b/src/Positions.xaml.cs index d5a901f1..fdb44202 100644 --- a/src/Positions.xaml.cs +++ b/src/Positions.xaml.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * * This software is provided 'as-is', without any express or implied From 52aeefabae910e5942c50db77b44b43d33422c18 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:08:25 -0800 Subject: [PATCH 029/276] refactor: remove unnecessary using System.Reflection --- src/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Program.cs b/src/Program.cs index 00441e0e..81055d08 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Reflection; using System.Threading.Tasks; using System.Windows; using HXE.HCE; From d85f3c6ffca203c3f9cae4fd31aa0cba8924d026 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:47:09 -0800 Subject: [PATCH 030/276] fix: add using System.Linq --- src/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Program.cs b/src/Program.cs index 81055d08..df5d6503 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -30,6 +30,7 @@ using static HXE.Console; using static HXE.Exit; using static HXE.Properties.Resources; +using System.Linq; namespace HXE { From c8950224de39cd8b4af1d9765380d755240ab2bf Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:57:03 -0800 Subject: [PATCH 031/276] feat: add --cli arg Opens CLI instead of GUI when available --- src/Program.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Program.cs b/src/Program.cs index df5d6503..276482da 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -60,6 +60,7 @@ public static void Main(string[] args) /// --test Start a dry run of HXE to self-test
/// --config Opens configuration GUI
/// --positions Opens first-person model positions GUI
+ /// --cli Opens CLI instead of GUI where available
/// --install=VALUE Installs HCE/SPV3 to destination
/// --compile=VALUE Compiles HCE/SPV3 to destination
/// --update=VALUE Updates directory with specified manifest
@@ -84,6 +85,7 @@ private static void InvokeProgram(string[] args) var test = false; /* Start a dry run of HXE to self-test */ var config = false; /* Opens configuration GUI */ var positions = false; /* Opens positions GUI */ + var cli = false; /* Opens CLI instead of GUI where available */ var install = string.Empty; /* Installs HCE/SPV3 to destination */ var compile = string.Empty; /* Compiles HCE/SPV3 to destination */ var update = string.Empty; /* Updates directory using manifest */ @@ -105,6 +107,7 @@ private static void InvokeProgram(string[] args) .Add("test", "Start a dry run of HXE to self-test", s => test =s != null) /* hxe command */ .Add("config", "Opens configuration GUI", s => config = s != null) /* hxe command */ .Add("positions", "Opens positions GUI", s => positions = s != null) /* hxe command */ + .Add("cli", "Enable CLI of Positions or Config", s => cli = s != null) /* hxe parameter */ .Add("install=", "Installs HCE/SPV3 to destination", s => install = s) /* hxe parameter */ .Add("compile=", "Compiles HCE/SPV3 to destination", s => compile = s) /* hxe parameter */ .Add("update=", "Updates directory using manifest", s => update = s) /* hxe parameter */ @@ -179,8 +182,15 @@ private static void InvokeProgram(string[] args) if (positions) { - _ = new Application().Run(new Positions()); - Exit(0); + if (cli) + { + CLI.Positions.Run(); + } + else + { + _ = new Application().Run(new Positions()); + } + WithCode(0); } if (infer) From b10ca0b876575f863cccb9e7da3e331fc6a856b9 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:58:19 -0800 Subject: [PATCH 032/276] refactor: move NewLine appendix in Exception output --- src/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Program.cs b/src/Program.cs index 276482da..cb4b680b 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -266,9 +266,9 @@ private static void InvokeProgram(string[] args) " -- Looked in working directory, Program Files, and Registry." + NewLine + " -- The working directory is " + CurrentDirectory + NewLine + " -- Error: " + NewLine + - e.ToString() + NewLine; + e.ToString(); var log = (File) Paths.Exception; - log.AppendAllText(msg); + log.AppendAllText(msg + NewLine); Error(msg); } From 10c392f2de6c84f7dcbe2520c0e6cee51b853506 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 19:58:56 -0800 Subject: [PATCH 033/276] refactor: change input prompt for --infer --- src/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Program.cs b/src/Program.cs index cb4b680b..b963bc25 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -206,7 +206,7 @@ private static void InvokeProgram(string[] args) }; Info($"Inferred the following Halo process: {descriptions[Process.Infer()]}"); - Info("Press any key to exit."); + Info("Press Enter to exit"); _ = ReadLine(); Exit(0); } From 23a0b284a4e00528b82000d04eb85c201d1cefa0 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:07:41 -0800 Subject: [PATCH 034/276] refactor: use Exit.WithCode() instead of Exit() --- src/Program.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Program.cs b/src/Program.cs index b963bc25..6f433ac7 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -134,7 +134,7 @@ private static void InvokeProgram(string[] args) if (help) { options.WriteOptionDescriptions(Out); - Exit(0); + WithCode(Code.Success); } if (test) @@ -172,12 +172,13 @@ private static void InvokeProgram(string[] args) { Error("Positions window threw an exception!" + NewLine + e.ToString()); } + WithCode(Code.Success); } if (config) { _ = new Application().Run(new Settings()); - Exit(0); + WithCode(Code.Success); } if (positions) @@ -190,7 +191,7 @@ private static void InvokeProgram(string[] args) { _ = new Application().Run(new Positions()); } - WithCode(0); + WithCode(Code.Success); } if (infer) @@ -208,7 +209,7 @@ private static void InvokeProgram(string[] args) Info($"Inferred the following Halo process: {descriptions[Process.Infer()]}"); Info("Press Enter to exit"); _ = ReadLine(); - Exit(0); + WithCode(Code.Success); } if (!string.IsNullOrWhiteSpace(install)) From cee7a4d2680a8f5a3488010abfc005b6f35369a3 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:13:26 -0800 Subject: [PATCH 035/276] fix: re-throw exceptions during --test --- src/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Program.cs b/src/Program.cs index 6f433ac7..0c9a36a1 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -154,6 +154,7 @@ private static void InvokeProgram(string[] args) catch (Exception e) { Error("Settings window threw an exception!" + NewLine + e.ToString()); + throw; } try @@ -171,6 +172,7 @@ private static void InvokeProgram(string[] args) catch (Exception e) { Error("Positions window threw an exception!" + NewLine + e.ToString()); + throw; } WithCode(Code.Success); } From b2624b7226874e1d5afadd9b220501dad61f9974 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:16:23 -0800 Subject: [PATCH 036/276] refactor: add const string ExceptionHeader --- src/Process.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Process.cs b/src/Process.cs index fe911839..bfcea91a 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2021 Emilian Roman * * This software is provided 'as-is', without any express or implied @@ -27,6 +27,7 @@ namespace HXE { public class Process { + private const string ExceptionHeader = " -- Process Inference failed"; public enum Type { Unknown, From 79e7762eecb040cd7b026cc60a9d12b3f2bcea63 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:13:59 -0800 Subject: [PATCH 037/276] style: sort usings --- src/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Program.cs b/src/Program.cs index 0c9a36a1..82479784 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using System.Windows; using HXE.HCE; @@ -30,7 +31,6 @@ using static HXE.Console; using static HXE.Exit; using static HXE.Properties.Resources; -using System.Linq; namespace HXE { From 551eb23492aa3762ab2e5608d5fde66da7e21e3d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:16:52 -0800 Subject: [PATCH 038/276] style: fix indentation --- src/Process.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index bfcea91a..e740ea06 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -39,13 +39,13 @@ public enum Type } public static IEnumerable Candidates { get; } = new List - { - new Candidate { Type = Type.Retail, Name = "halo" }, - new Candidate { Type = Type.HCE, Name = "haloce" }, - new Candidate { Type = Type.Steam, Name = "MCC-Win64-Shipping" }, - new Candidate { Type = Type.StoreOld, Name = "MCC-Win64-Shipping-WinStore" }, - new Candidate { Type = Type.Store, Name = "MCCWinStore-Win64-Shipping" } - }; + { + new Candidate { Type = Type.Retail, Name = "halo" }, + new Candidate { Type = Type.HCE, Name = "haloce" }, + new Candidate { Type = Type.Steam, Name = "MCC-Win64-Shipping" }, + new Candidate { Type = Type.StoreOld, Name = "MCC-Win64-Shipping-WinStore" }, + new Candidate { Type = Type.Store, Name = "MCCWinStore-Win64-Shipping" } + }; /// /// Infers the running Halo executable, with support for HCE, HCE and MCC (Steam & Windows Store). From 304dcecd655a62d665c9ff8f41bf1f051ef24fbd Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:14:57 -0800 Subject: [PATCH 039/276] docs: add TODO regarding --silent --- src/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Program.cs b/src/Program.cs index 82479784..bb40a6ca 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -77,6 +77,7 @@ public static void Main(string[] args) /// --vidmode=VALUE Loads HCE with custom res. and Hz
/// --refresh=VALUE Loads HCE with custom refresh rate
/// + /// TODO: implement --silent to run CLI without user prompts; private static void InvokeProgram(string[] args) { Directory.CreateDirectory(Paths.Directory); From bc4f178621a38b98be2a16bf454c9f4d92d06fe5 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:25:32 -0800 Subject: [PATCH 040/276] refactor: add Results list for returning result type and message --- src/Process.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Process.cs b/src/Process.cs index e740ea06..3a17f642 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -47,6 +47,21 @@ public enum Type new Candidate { Type = Type.Store, Name = "MCCWinStore-Win64-Shipping" } }; + public static IEnumerable Results { get; } = new List + { + new Result { Success = true, Message = "Found Halo Retail/CD or Halo Custom Edition"}, + new Result { Success = true, Message = "Found MCC with CEA DLC"}, + new Result { Success = false, Message = "Found MCC, but CEA DLC is missing"}, + new Result { Success = false, Message = + "No running processes matched the following criteria:" + NewLine + + "halo.exe v1.0.10.621" + NewLine + + "haloce.exe v1.0.10.621" + NewLine + + "MCC-Win64-Shipping.exe with CEA DLC" + NewLine + + "MCC-Win64-Shipping-WinStore.exe with CEA DLC" + NewLine + + "MCCWinStore-Win64-Shipping.exe with CEA DLC"}, + new Result { Success = false, Message = "Unknown error occurred."} + }; + /// /// Infers the running Halo executable, with support for HCE, HCE and MCC (Steam & Windows Store). /// @@ -139,5 +154,11 @@ public class Candidate public Type Type { get; set; } public string Name { get; set; } } + + public class Result + { + public bool Success { get; set; } + public string Message { get; set; } + } } -} \ No newline at end of file +} From 5e90c47313612bb812f1fd3c23819e0a30c5dbcb Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:29:48 -0800 Subject: [PATCH 041/276] refactor: consolidate Process' error output --- src/Process.cs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index 3a17f642..1af03406 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -77,11 +77,7 @@ public static Type Infer() } catch (System.Exception e) { - var msg = $" -- Process Inference failed{NewLine}Error: { e }{NewLine}"; - var log = (File) Paths.Exception; - log.AppendAllText(msg); - Console.Info(msg); - throw; + ErrorOutput(e, ""); } return processCandidate?.Type ?? Type.Unknown; @@ -128,10 +124,9 @@ private static bool DeeperCheck(System.Diagnostics.Process process, string candi } catch (System.Exception e) { - var msg2 = string.Empty; - msg2 += Is64BitProcess ? "Current process is 64-bit." : "Current process is not 32-bit."; - msg2 += NewLine; - msg2 += Is64BitOperatingSystem ? "Operating system is 64-bit." : "Operating system is NOT 64-bit."; + string msg2 = "MCC process found, but failed to inspect loaded modules for halo1.dll" + NewLine + + (Is64BitProcess ? "Current process is 64-bit." : "Current process is not 32-bit.") + NewLine + + (Is64BitOperatingSystem ? "Operating system is 64-bit." : "Operating system is NOT 64-bit."); ErrorOutput(e, msg2); return false; } @@ -140,13 +135,15 @@ private static bool DeeperCheck(System.Diagnostics.Process process, string candi default: return false; } - void ErrorOutput(System.Exception e, string msg2) - { - var msg = $" -- Process Inference failed{NewLine}{msg2}{NewLine}Error: { e }{NewLine}"; - var log = (File) Paths.Exception; - log.AppendAllText(msg); - Console.Error(msg); ; - } + } + + private static void ErrorOutput(System.Exception e, string msg2) + { + string msg = ExceptionHeader + NewLine + + msg2 + NewLine + + "Error: " + e.ToString(); + ((File)Paths.Exception).AppendAllText(msg + NewLine); + Console.Error(msg); } public class Candidate From 5967dbdb1ad65a389a88a22d78f55d3535319d83 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:40:00 -0800 Subject: [PATCH 042/276] refactor: add class `ResultAndType` --- src/Process.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Process.cs b/src/Process.cs index 1af03406..30aaa464 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -157,5 +157,11 @@ public class Result public bool Success { get; set; } public string Message { get; set; } } + + public class ResultAndType + { + public Result Result { get; set; } + public Type Type { get; set; } + } } } From f7b7aaef55896eb17ec3456304dbbd5ee33893c2 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:41:03 -0800 Subject: [PATCH 043/276] docs: update Infer()'s summary --- src/Process.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Process.cs b/src/Process.cs index 30aaa464..71f260a9 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -63,7 +63,7 @@ public enum Type }; /// - /// Infers the running Halo executable, with support for HCE, HCE and MCC (Steam & Windows Store). + /// Infers the running Halo executable, with support for Halo Retail, Halo Custom Edition, and MCC (Steam & Windows Store). /// /// Type of Platform public static Type Infer() From 0749529e93f48ae05f24b2e3971891497aac069a Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:43:27 -0800 Subject: [PATCH 044/276] refactor: move process matching from Infer() to DeeperCheck() --- src/Process.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index 71f260a9..a52411a7 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -68,12 +68,11 @@ public enum Type /// Type of Platform public static Type Infer() { - var processCandidate = new Candidate(); + Candidate processCandidate = null; + List processList = GetProcesses().ToList(); try { - processCandidate = Candidates - .FirstOrDefault(x => DeeperCheck(GetProcesses() - .FirstOrDefault(Processname => Processname.ProcessName == x.Name), x.Name)); + processCandidate = Candidates.First(x => DeeperCheck(x, processList)); } catch (System.Exception e) { @@ -83,16 +82,16 @@ public static Type Infer() return processCandidate?.Type ?? Type.Unknown; } - private static bool DeeperCheck(System.Diagnostics.Process process, string candidateName) + private static bool DeeperCheck(Candidate candidate, List processList) { - /** Check for NullReferenceException (no processes match current candidate) */ + System.Diagnostics.Process process; try { - bool check = process.ProcessName == candidateName; + process = processList.First(p => p.ProcessName == candidate.Name); } - catch (System.NullReferenceException) + catch (System.InvalidOperationException) { - return false; + return false; /// No processes match current candidate } switch (process.ProcessName) From c3bf9e36cead6d7312c6e91447259e87abe014f7 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 20:46:04 -0800 Subject: [PATCH 045/276] refactor: set Process to static BREAKING CHANGE --- src/Process.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Process.cs b/src/Process.cs index a52411a7..fc9fe8ef 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -25,7 +25,7 @@ namespace HXE { - public class Process + public static class Process { private const string ExceptionHeader = " -- Process Inference failed"; public enum Type From 01c7ff187f1fa9f09300195ae6be0ddda958182e Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 21:45:57 -0800 Subject: [PATCH 046/276] refactor: combine Result and ResultAndType --- src/Process.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index fc9fe8ef..7dcc65b9 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -154,13 +154,8 @@ public class Candidate public class Result { public bool Success { get; set; } - public string Message { get; set; } - } - - public class ResultAndType - { - public Result Result { get; set; } public Type Type { get; set; } + public string Message { get; set; } } } } From f286be2972efeca26559a1dd3ce06edb768ece86 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 19 Feb 2022 23:44:41 -0800 Subject: [PATCH 047/276] refactor: add LastResult for random access and returned values --- src/Process.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Process.cs b/src/Process.cs index 7dcc65b9..77125f81 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -62,6 +62,8 @@ public enum Type new Result { Success = false, Message = "Unknown error occurred."} }; + public static Result LastResult { get; internal set; } // TODO: Upon success/failure, update CurrentStatus with appropriate Status from Results + /// /// Infers the running Halo executable, with support for Halo Retail, Halo Custom Edition, and MCC (Steam & Windows Store). /// From 44703fcd9b0999135e8f7b5fc9ec4a6ce6be454e Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 02:12:30 -0800 Subject: [PATCH 048/276] refactor: move Halo PC workload to InspectHPC() This allows halo and haloce cases to set LastResult members without one case falling through to the other. --- src/Process.cs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index 77125f81..6679f554 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -99,18 +99,13 @@ private static bool DeeperCheck(Candidate candidate, List Date: Sun, 20 Feb 2022 02:30:38 -0800 Subject: [PATCH 049/276] refactor: move MCC workload to InspectMCC() --- src/Process.cs | 51 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index 6679f554..0bdbbdc9 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -110,23 +110,13 @@ private static bool DeeperCheck(Candidate candidate, List() - .Any(module => module.ModuleName == Paths.MCC.H1dll); - } - catch (System.Exception e) - { - string msg2 = "MCC process found, but failed to inspect loaded modules for halo1.dll" + NewLine - + (Is64BitProcess ? "Current process is 64-bit." : "Current process is not 32-bit.") + NewLine - + (Is64BitOperatingSystem ? "Operating system is 64-bit." : "Operating system is NOT 64-bit."); - ErrorOutput(e, msg2); - return false; - } - } + LastResult.Success = InspectMCC(); + LastResult.Type = Type.Steam; + return LastResult.Success; default: return false; @@ -145,7 +135,34 @@ bool InspectHPC() catch (System.Exception e) { const string msg = "Failed to assess Halo/HaloCE process"; - LastResult.Success = false; + LastResult.Message = msg; + ErrorOutput(e, msg); + return false; + } + } + + bool InspectMCC() + { + try + { + bool isValid = process.Modules + .Cast() + .Any(module => module.ModuleName == Paths.MCC.H1dll); + LastResult.Message = "Found MCC process with halo1.dll loaded"; + return isValid; + } + catch (System.ComponentModel.Win32Exception e) + { + string msg = "MCC process found, but cannot inspect its modules because 32-bit processes cannot inspect 64-bit processes" + NewLine + + (Is64BitProcess ? "Current process is 64-bit." : "Current process NOT 64-bit.") + NewLine + + (Is64BitOperatingSystem ? "Operating system is 64-bit." : "Operating system is NOT 64-bit."); + LastResult.Message = msg; + ErrorOutput(e, msg); + return false; + } + catch (System.Exception e) + { + const string msg = "MCC process found, but failed to inspect loaded modules for halo1.dll for an unknown reason"; LastResult.Message = msg; ErrorOutput(e, msg); return false; From b878bdd37342ba480118460856c6f524423ab4a4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 02:33:50 -0800 Subject: [PATCH 050/276] refactor: set LastResult members in Infer() --- src/Process.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Process.cs b/src/Process.cs index 0bdbbdc9..3fbb4269 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -79,9 +79,29 @@ public static Type Infer() catch (System.Exception e) { ErrorOutput(e, ""); + LastResult.Success = false; + LastResult.Type = Type.Unknown; + LastResult.Message = "An unhandled exception occurred" + NewLine + e.ToString(); } - return processCandidate?.Type ?? Type.Unknown; + if (processCandidate?.Type == null) + { + LastResult.Success = false; + LastResult.Type = Type.Unknown; + LastResult.Message = + "No running processes matched the following criteria:" + NewLine + + "halo.exe v1.0.10.621" + NewLine + + "haloce.exe v1.0.10.621" + NewLine + + "MCC-Win64-Shipping.exe with CEA DLC" + NewLine + + "MCC-Win64-Shipping-WinStore.exe with CEA DLC" + NewLine + + "MCCWinStore-Win64-Shipping.exe with CEA DLC"; + return Type.Unknown; + } + else + { + /// LastResult has already been set by DeeperCheck() + return processCandidate.Type; + } } private static bool DeeperCheck(Candidate candidate, List processList) From 3215d6170c611ca838c650f057e844fa781d68fd Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 02:39:05 -0800 Subject: [PATCH 051/276] refactor: remove unused Results list --- src/Process.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Process.cs b/src/Process.cs index 3fbb4269..7f6f0265 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -47,21 +47,6 @@ public enum Type new Candidate { Type = Type.Store, Name = "MCCWinStore-Win64-Shipping" } }; - public static IEnumerable Results { get; } = new List - { - new Result { Success = true, Message = "Found Halo Retail/CD or Halo Custom Edition"}, - new Result { Success = true, Message = "Found MCC with CEA DLC"}, - new Result { Success = false, Message = "Found MCC, but CEA DLC is missing"}, - new Result { Success = false, Message = - "No running processes matched the following criteria:" + NewLine + - "halo.exe v1.0.10.621" + NewLine + - "haloce.exe v1.0.10.621" + NewLine + - "MCC-Win64-Shipping.exe with CEA DLC" + NewLine + - "MCC-Win64-Shipping-WinStore.exe with CEA DLC" + NewLine + - "MCCWinStore-Win64-Shipping.exe with CEA DLC"}, - new Result { Success = false, Message = "Unknown error occurred."} - }; - public static Result LastResult { get; internal set; } // TODO: Upon success/failure, update CurrentStatus with appropriate Status from Results /// From d3d6ae526aec49614d616a478a7a5180da44af11 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 02:40:25 -0800 Subject: [PATCH 052/276] docs: add summary of DeeperCheck's switch statement --- src/Process.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Process.cs b/src/Process.cs index 7f6f0265..49874981 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -101,6 +101,12 @@ private static bool DeeperCheck(Candidate candidate, List Date: Sun, 20 Feb 2022 02:43:34 -0800 Subject: [PATCH 053/276] feat: add InferResult() ...which makes use of recent Result additions. InferResult() is an informative alternative to Infer. Infer() is kept for compatibility. --- src/Process.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Process.cs b/src/Process.cs index 49874981..1eef8359 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -49,6 +49,12 @@ public enum Type public static Result LastResult { get; internal set; } // TODO: Upon success/failure, update CurrentStatus with appropriate Status from Results + public static Result InferResult() + { + _ = Infer(); + return LastResult; + } + /// /// Infers the running Halo executable, with support for Halo Retail, Halo Custom Edition, and MCC (Steam & Windows Store). /// From 055caede80fee7ce411a59acce909f5808fd2a62 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 03:05:54 -0800 Subject: [PATCH 054/276] docs: add summary and return docs to InferResult() --- src/Process.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Process.cs b/src/Process.cs index 1eef8359..8e29b56b 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -49,6 +49,11 @@ public enum Type public static Result LastResult { get; internal set; } // TODO: Upon success/failure, update CurrentStatus with appropriate Status from Results + /// + /// An informative alternative to Infer()
+ /// Infers the running Halo executable, with support for Halo Retail, Halo Custom Edition, and MCC (Steam & Windows Store) + ///
+ /// , a static instance of that was updated by Infer() and its called method(s) public static Result InferResult() { _ = Infer(); From 45f2105d845a4c75eb49c041f759fb4f6088e935 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 03:06:28 -0800 Subject: [PATCH 055/276] docs: remove old TODO note --- src/Process.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Process.cs b/src/Process.cs index 8e29b56b..2c9334d8 100644 --- a/src/Process.cs +++ b/src/Process.cs @@ -47,7 +47,7 @@ public enum Type new Candidate { Type = Type.Store, Name = "MCCWinStore-Win64-Shipping" } }; - public static Result LastResult { get; internal set; } // TODO: Upon success/failure, update CurrentStatus with appropriate Status from Results + public static Result LastResult { get; internal set; } /// /// An informative alternative to Infer()
From 19f6403748f807dc787f8c7bd783b8f6a97ae649 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 03:09:13 -0800 Subject: [PATCH 056/276] style: add end-of-file newline --- src/Common/ExtProcess.cs | 4 ++-- src/Settings.xaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Common/ExtProcess.cs b/src/Common/ExtProcess.cs index 017a785b..8a1a13b8 100644 --- a/src/Common/ExtProcess.cs +++ b/src/Common/ExtProcess.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * Copyright (c) 2021 Noah Sherwin * @@ -31,4 +31,4 @@ public static bool RunningAsAdmin() return Principle.IsInRole(WindowsBuiltInRole.Administrator); } } -} \ No newline at end of file +} diff --git a/src/Settings.xaml b/src/Settings.xaml index 04ee2f27..2bc84434 100644 --- a/src/Settings.xaml +++ b/src/Settings.xaml @@ -1,4 +1,4 @@ - + + + Release + Any CPU + false + ..\bin\Release\net462\publish\ + FileSystem + net462 + + diff --git a/src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml b/src/Properties/PublishProfiles/Release_net6.0-windows_fdd.pubxml similarity index 64% rename from src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml rename to src/Properties/PublishProfiles/Release_net6.0-windows_fdd.pubxml index efa692e9..2b1bae69 100644 --- a/src/Properties/PublishProfiles/Release_net6.0-windows_framework-dependent.pubxml +++ b/src/Properties/PublishProfiles/Release_net6.0-windows_fdd.pubxml @@ -10,8 +10,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem net6.0-windows false - true - true - + true + true diff --git a/src/Properties/PublishProfiles/Release_net6.0-windows_win7-x86.pubxml b/src/Properties/PublishProfiles/Release_net6.0-windows_win7-x86.pubxml new file mode 100644 index 00000000..7d553ca3 --- /dev/null +++ b/src/Properties/PublishProfiles/Release_net6.0-windows_win7-x86.pubxml @@ -0,0 +1,17 @@ + + + + + Release + Any CPU + ..\bin\Release\net6.0-windows\win7-x86\publish\ + FileSystem + net6.0-windows + win7-x86 + true + false + $('$(Win7SF)' == 'true') + + From b1059cbb71fc1e3e5343d726354db06be295a115 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 16:11:39 -0800 Subject: [PATCH 065/276] fix: handle InvalidOperationException sometimes thrown when setting DialogResult The two causes of this exception have been added as doc comments where it is caught. resolves HaloSPV3/HXE#248 --- src/Settings.xaml.cs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Settings.xaml.cs b/src/Settings.xaml.cs index 7fa3b1b7..aa7ba9ee 100644 --- a/src/Settings.xaml.cs +++ b/src/Settings.xaml.cs @@ -157,7 +157,19 @@ private void Save(object sender, RoutedEventArgs e) } else { - DialogResult = true; + try + { + DialogResult = true; + } + catch (InvalidOperationException ex) when (ex.Message == "DialogResult can be set only after Window is created and shown as dialog.") + { + // This exception can't be prevented nor handled in any other way... + /** Exceptions + * InvalidOperationException + * DialogResult is set before a window is opened by calling ShowDialog(). + * -or- + * DialogResult is set on a window that is opened by calling Show(). + */ } Close(); } } @@ -165,8 +177,27 @@ private void Save(object sender, RoutedEventArgs e) private void Cancel(object sender, RoutedEventArgs e) { if (SettingsCore.ProcessName == "hxe") + { Exit.WithCode(Exit.Code.Success); - else Close(); + } + else + { + try + { + DialogResult = false; + } + catch (InvalidOperationException ex) when (ex.Message == "DialogResult can be set only after Window is created and shown as dialog.") + { + // This exception can't be prevented nor handled in any other way... + /** Exceptions + * InvalidOperationException + * DialogResult is set before a window is opened by calling ShowDialog(). + * -or- + * DialogResult is set on a window that is opened by calling Show(). + */ + } + Close(); + } } } } From 9c0011ce04743665c5fea8129fe6e6fc2a269191 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 16:19:02 -0800 Subject: [PATCH 066/276] docs: update banner.txt's example arguments --- src/Assets/banner.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Assets/banner.txt b/src/Assets/banner.txt index aadd6c4a..4f32f10f 100644 --- a/src/Assets/banner.txt +++ b/src/Assets/banner.txt @@ -12,12 +12,16 @@ A HCE wrapper and kernel for SPV3 HXE can be invoked with the following arguments: + --help Displays commands list + --test Start a dry run of HXE to self-test --config Opens configuration GUI --positions Opens positions GUI - --load Initiates HCE/SPV3 + --cli Opens CLI instead of GUI where available --install=VALUE Installs HCE/SPV3 to destination --compile=VALUE Compiles HCE/SPV3 to destination --update=VALUE Updates directory using manifest + --registry=VALUE Write to Windows Registry + --infer Infer the running Halo executable --console Loads HCE with console mode --devmode Loads HCE with developer mode --screenshot Loads HCE with screenshot ability @@ -25,4 +29,6 @@ HXE can be invoked with the following arguments: --nogamma Loads HCE without gamma overriding --adapter=VALUE Loads HCE on monitor X --path=VALUE Loads HCE with custom profile path - --vidmode=VALUE Loads HCE with video mode \ No newline at end of file + --exec=VALUE Loads HCE with custom init file + --vidmode=VALUE Loads HCE with video mode + --refresh=VALUE Loads HCE with custom refresh rate From 3287c0bd8db507888d93fbad3c4220d8daf78009 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 19:29:22 -0800 Subject: [PATCH 067/276] refactor: convert banner.txt to a const string File-type Resources are frustrating. They also require WinForms. Why? --- src/Assets/banner.txt | 34 ------------------- src/HXE.csproj | 1 - src/Program.cs | 37 ++++++++++++++++++++ src/Properties/Resources.resx | 63 +++++++++++++++++------------------ 4 files changed, 67 insertions(+), 68 deletions(-) delete mode 100644 src/Assets/banner.txt diff --git a/src/Assets/banner.txt b/src/Assets/banner.txt deleted file mode 100644 index 4f32f10f..00000000 --- a/src/Assets/banner.txt +++ /dev/null @@ -1,34 +0,0 @@ - _ ___ ________ -| | | \ \ / / ____| -| |__| |\ V /| |__ -| __ | > < | __| -| | | |/ . \| |____ -|_| |_/_/ \_\______| :: Halo XE -================================= -A HCE wrapper and kernel for SPV3 ---------------------------------- -:: https://github.com/HaloSPV3/hxe ---------------------------------- - -HXE can be invoked with the following arguments: - - --help Displays commands list - --test Start a dry run of HXE to self-test - --config Opens configuration GUI - --positions Opens positions GUI - --cli Opens CLI instead of GUI where available - --install=VALUE Installs HCE/SPV3 to destination - --compile=VALUE Compiles HCE/SPV3 to destination - --update=VALUE Updates directory using manifest - --registry=VALUE Write to Windows Registry - --infer Infer the running Halo executable - --console Loads HCE with console mode - --devmode Loads HCE with developer mode - --screenshot Loads HCE with screenshot ability - --window Loads HCE in window mode - --nogamma Loads HCE without gamma overriding - --adapter=VALUE Loads HCE on monitor X - --path=VALUE Loads HCE with custom profile path - --exec=VALUE Loads HCE with custom init file - --vidmode=VALUE Loads HCE with video mode - --refresh=VALUE Loads HCE with custom refresh rate diff --git a/src/HXE.csproj b/src/HXE.csproj index bc00ded4..d276da8c 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -104,7 +104,6 @@ %(FileName).xml - diff --git a/src/Program.cs b/src/Program.cs index bb40a6ca..5932e821 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -39,6 +39,43 @@ namespace HXE ///
internal static class Program { + internal const string Banner = @" + _ ___ ________ +| | | \ \ / / ____| +| |__| |\ V /| |__ +| __ | > < | __| +| | | |/ . \| |____ +|_| |_/_/ \_\______| :: Halo XE +================================= +A HCE wrapper and kernel for SPV3 +--------------------------------- +:: https://github.com/HaloSPV3/hxe +--------------------------------- + +HXE can be invoked with the following arguments: + + --help Displays commands list + --test Start a dry run of HXE to self-test + --config Opens configuration GUI + --positions Opens positions GUI + --cli Opens CLI instead of GUI where available + --install=VALUE Installs HCE/SPV3 to destination + --compile=VALUE Compiles HCE/SPV3 to destination + --update=VALUE Updates directory using manifest + --registry=VALUE Write to Windows Registry + --infer Infer the running Halo executable + --console Loads HCE with console mode + --devmode Loads HCE with developer mode + --screenshot Loads HCE with screenshot ability + --window Loads HCE in window mode + --nogamma Loads HCE without gamma overriding + --adapter=VALUE Loads HCE on monitor X + --path=VALUE Loads HCE with custom profile path + --exec=VALUE Loads HCE with custom init file + --vidmode=VALUE Loads HCE with video mode + --refresh=VALUE Loads HCE with custom refresh rate +"; + /// /// HXE entry. /// diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx index fa6a072f..1c0e3581 100644 --- a/src/Properties/Resources.resx +++ b/src/Properties/Resources.resx @@ -1,17 +1,17 @@ - @@ -112,15 +112,11 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Assets\banner.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - This binary has been compiled using commit {0} @@ -130,6 +126,7 @@ https://github.com/HaloSPV3/HXE/release/v{0} + ..\..\ext\halo-ce-patches\patches.crk;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 From dcf555c9ff163730b1d5be08539fc20cfda9f75d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:09:28 -0800 Subject: [PATCH 068/276] refactor: remove unused look-up of Banner resource --- src/Properties/Resources.Designer.cs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index 6e6bf282..fb87864f 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -60,32 +60,6 @@ internal Resources() { } } - /// - /// Looks up a localized string similar to "
- /// _ ___ ________
- ///| | | \ \ / / ____|
- ///| |__| |\ V /| |__
- ///| __ | > < | __|
- ///| | | |/ . \| |____
- ///|_| |_/_/ \_\______| :: Halo XE
- ///=================================
- ///A HCE wrapper and kernel for SPV3
- ///---------------------------------
- ///:: https://github.com/HaloSPV3/hxe
- ///---------------------------------
- ///
- ///HXE can be invoked with the following arguments:
- ///
- /// --config Opens configuration GUI
- /// --positions Opens positions GUI
- /// --load Initiat [rest of string was truncated]";.
- ///
- internal static string Banner { - get { - return ResourceManager.GetString("Banner", resourceCulture); - } - } - /// /// Looks up a localized string similar to This binary has been compiled using build-{0}. /// From 20470e4e15457d2c36c6871d0e10ff17c85a489d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 23:48:41 -0800 Subject: [PATCH 069/276] refactor: include patches.crk as EmbeddedResource separate from Resources.resx resolves HaloSPV3/HXE#250 --- src/HXE.csproj | 4 ++++ src/Patcher.cs | 35 ++++++++++++++++++---------- src/Properties/Resources.Designer.cs | 24 ------------------- src/Properties/Resources.resx | 4 ---- 4 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index d276da8c..616f10e8 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -104,6 +104,10 @@ %(FileName).xml + + Assets\%(Filename)%(Extension) + Never + diff --git a/src/Patcher.cs b/src/Patcher.cs index dcfd766c..236846be 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -42,30 +42,41 @@ public static List Reader() { /* Get patches.crk resource from assembly resources */ /* Read strings from memory to...array? */ + string rn = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames().First(r => r.EndsWith("patches.crk")); // probably "HXE.Assets.patches.crk" var nl = new string[]{ "\r\n" }; var byteSep = new string[]{": ", " "}; var list = new List(); var patchGroup = new PatchGroup(); - var file = Properties.Resources.patches.Split(nl, StringSplitOptions.RemoveEmptyEntries).ToList(); + List fileText = null; + using (Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(rn)) + using (var sr = new StreamReader(s, detectEncodingFromByteOrderMarks: true)) + { + fileText = sr.ReadToEnd().Split(nl, StringSplitOptions.None).ToList(); + } + + if (fileText == null) + { + throw new IOException("Failed to read contents of embedded resource 'patches.crk'"); + } - file.RemoveAt(0); + fileText.RemoveAt(0); /* Remove comments from list */ { var file2 = new List(); - foreach (var line in file) + foreach (var line in fileText) { if (!line.StartsWith(";")) file2.Add(line); } - file = file2; + fileText = file2; } /* Add Name, exe, and patch data to list */ - for (var index = 0; index < file.Count; index++) + for (var index = 0; index < fileText.Count; index++) { /// If the first char in the line is a letter... */ - while (index < file.Count && char.IsLetter(file.ElementAt(index).ToCharArray().First())) + while (index < fileText.Count && char.IsLetter(fileText.ElementAt(index).ToCharArray().First())) { /** ...skip Name and Executable. Go to patch data. */ index += 2; @@ -73,20 +84,20 @@ public static List Reader() /** ...assign patch name, */ /** ...assign filename, */ /** ...and then read patch data. */ - while (index < file.Count && char.IsDigit(file.ElementAt(index).ToCharArray().First())) + while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) { patchGroup = new PatchGroup() { DataSets = new List(), - Name = file.ElementAt(index - 2), - Executable = file.ElementAt(index - 1) }; + Name = fileText.ElementAt(index - 2), + Executable = fileText.ElementAt(index - 1) }; - while (index < file.Count && char.IsDigit(file.ElementAt(index).ToCharArray().First())) + while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) { /** Read Patch Data to List, ... * Assign values{offset, original, patch} * proceed to next Patch Data, * then check if line is Patch Data */ - List values = file. + List values = fileText. ElementAt(index).Split(byteSep, StringSplitOptions.RemoveEmptyEntries). ToList(); patchGroup.DataSets.Add(new DataSet { Offset = int.Parse(values[0], System.Globalization.NumberStyles.HexNumber), diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index fb87864f..42430a2c 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -87,30 +87,6 @@ internal static string BannerBuildSourceRelease { } } - /// - /// Looks up a localized string similar to Patches for Halo CE v1.0.10 - ///;orig SHA256: FEEA46FCE285EC071016CF5534ABE47ECF36F6CFAC8F1973EE6919851EA5A037 - /// - ///Make large address aware - ///haloce.exe - ///;----------------------- - ///;Normally 32-bit applications can only use up to 2GB of RAM. This patch increases that limit to 4GB. - ///;It is required for some maps and mods. - ///; - ///;Set the IMAGE_FILE_LARGE_ADDRESS_AWARE characteristic flag in the PE header - ///00000136: 0F 2F - /// - ///Remove DRM and key checks - ///haloce.exe - ///;------------------------ - ///;Allows playing and hosting [rest of string was truncated]";. - /// - internal static string patches { - get { - return ResourceManager.GetString("patches", resourceCulture); - } - } - /// /// Looks up a localized string similar to .. /// diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx index 1c0e3581..6be35e2d 100644 --- a/src/Properties/Resources.resx +++ b/src/Properties/Resources.resx @@ -126,10 +126,6 @@ https://github.com/HaloSPV3/HXE/release/v{0} - - - ..\..\ext\halo-ce-patches\patches.crk;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - . From 5bd4d031093bdb5ebd208516e89f3f3192cfe36c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 23:52:17 -0800 Subject: [PATCH 070/276] style: format document --- src/Patcher.cs | 526 +++++++++++++++++++++++++------------------------ 1 file changed, 266 insertions(+), 260 deletions(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index 236846be..0efea713 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -6,47 +6,47 @@ namespace HXE { - public class Patcher - { - /// Read patches.crk to Patches list - /// foreach containing exe:haloce.exe, get and apply requested patches. - /// if a patch is no longer requested (enabled), restore the original value. - /// Patches will be passed to and stored in Kernel via bit-wise integer. - /// Later, some patches may be configured in SPV3 loader. Perhaps via The "Advanced" menu aka HXE's Configuration UserControl. Bring it full circle. - public class PatchGroup + public class Patcher { - public string Name { get; set; } = string.Empty; /** Make large address aware */ - public string Executable { get; set; } = string.Empty; /** haloce.exe */ - public bool Toggle { get; set; } = false; /** Patch/Restore values */ - public List DataSets = new List(); - } + /// Read patches.crk to Patches list + /// foreach containing exe:haloce.exe, get and apply requested patches. + /// if a patch is no longer requested (enabled), restore the original value. + /// Patches will be passed to and stored in Kernel via bit-wise integer. + /// Later, some patches may be configured in SPV3 loader. Perhaps via The "Advanced" menu aka HXE's Configuration UserControl. Bring it full circle. + public class PatchGroup + { + public string Name { get; set; } = string.Empty; /** Make large address aware */ + public string Executable { get; set; } = string.Empty; /** haloce.exe */ + public bool Toggle { get; set; } = false; /** Patch/Restore values */ + public List DataSets = new List(); + } - public class DataSet // 00000136: 0F 2F - { - public long Offset { get; set; } - public byte Original { get; set; } - public byte Patch { get; set; } - } + public class DataSet // 00000136: 0F 2F + { + public long Offset { get; set; } + public byte Original { get; set; } + public byte Patch { get; set; } + } - /// - /// The list of patch groups specified by pR0ps' halo ce patches. - /// - public static List Patches = Reader(); // See Write() for tmp overrides + /// + /// The list of patch groups specified by pR0ps' halo ce patches. + /// + public static List Patches = Reader(); // See Write() for tmp overrides - /// - /// Reads PatchGroups from patches.crk to this instance. - /// - /// A list of PatchGroups read from the patches.crk file resource. - /// This is only used for Patches list initialization. - public static List Reader() - { - /* Get patches.crk resource from assembly resources */ - /* Read strings from memory to...array? */ + /// + /// Reads PatchGroups from patches.crk to this instance. + /// + /// A list of PatchGroups read from the patches.crk file resource. + /// This is only used for Patches list initialization. + public static List Reader() + { + /* Get patches.crk resource from assembly resources */ + /* Read strings from memory to...array? */ string rn = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames().First(r => r.EndsWith("patches.crk")); // probably "HXE.Assets.patches.crk" - var nl = new string[]{ "\r\n" }; - var byteSep = new string[]{": ", " "}; - var list = new List(); - var patchGroup = new PatchGroup(); + var nl = new string[] { "\r\n" }; + var byteSep = new string[] { ": ", " " }; + var list = new List(); + var patchGroup = new PatchGroup(); List fileText = null; using (Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(rn)) using (var sr = new StreamReader(s, detectEncodingFromByteOrderMarks: true)) @@ -59,255 +59,261 @@ public static List Reader() throw new IOException("Failed to read contents of embedded resource 'patches.crk'"); } - fileText.RemoveAt(0); - - /* Remove comments from list */ - { - var file2 = new List(); - foreach (var line in fileText) - { - if (!line.StartsWith(";")) - file2.Add(line); - } - fileText = file2; - } + fileText.RemoveAt(0); - /* Add Name, exe, and patch data to list */ - for (var index = 0; index < fileText.Count; index++) - { - /// If the first char in the line is a letter... */ - while (index < fileText.Count && char.IsLetter(fileText.ElementAt(index).ToCharArray().First())) - { - /** ...skip Name and Executable. Go to patch data. */ - index += 2; - /** Then, if the line is patch data... */ - /** ...assign patch name, */ - /** ...assign filename, */ - /** ...and then read patch data. */ - while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) - { - patchGroup = new PatchGroup() { DataSets = new List(), - Name = fileText.ElementAt(index - 2), - Executable = fileText.ElementAt(index - 1) }; + /* Remove comments from list */ + { + var file2 = new List(); + foreach (var line in fileText) + { + if (!line.StartsWith(";")) + file2.Add(line); + } + fileText = file2; + } - while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) + /* Add Name, exe, and patch data to list */ + for (var index = 0; index < fileText.Count; index++) { - /** Read Patch Data to List, ... - * Assign values{offset, original, patch} - * proceed to next Patch Data, - * then check if line is Patch Data - */ - List values = fileText. - ElementAt(index).Split(byteSep, StringSplitOptions.RemoveEmptyEntries). - ToList(); - patchGroup.DataSets.Add(new DataSet { Offset = int.Parse(values[0], System.Globalization.NumberStyles.HexNumber), - Original = byte.Parse(values[1], System.Globalization.NumberStyles.HexNumber), - Patch = byte.Parse(values[2], System.Globalization.NumberStyles.HexNumber)}); - index++; + /// If the first char in the line is a letter... */ + while (index < fileText.Count && char.IsLetter(fileText.ElementAt(index).ToCharArray().First())) + { + /** ...skip Name and Executable. Go to patch data. */ + index += 2; + /** Then, if the line is patch data... */ + /** ...assign patch name, */ + /** ...assign filename, */ + /** ...and then read patch data. */ + while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) + { + patchGroup = new PatchGroup() + { + DataSets = new List(), + Name = fileText.ElementAt(index - 2), + Executable = fileText.ElementAt(index - 1) + }; + + while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) + { + /** Read Patch Data to List, ... + * Assign values{offset, original, patch} + * proceed to next Patch Data, + * then check if line is Patch Data + */ + List values = fileText. + ElementAt(index).Split(byteSep, StringSplitOptions.RemoveEmptyEntries). + ToList(); + patchGroup.DataSets.Add(new DataSet + { + Offset = int.Parse(values[0], System.Globalization.NumberStyles.HexNumber), + Original = byte.Parse(values[1], System.Globalization.NumberStyles.HexNumber), + Patch = byte.Parse(values[2], System.Globalization.NumberStyles.HexNumber) + }); + index++; + } + } + list.Add(patchGroup); + } } - } - list.Add(patchGroup); - } - } - /* Parse the data to members "PatchGroup" of list "Patches" */ + /* Parse the data to members "PatchGroup" of list "Patches" */ - /* return list */ - return list; - } + /* return list */ + return list; + } - /// - /// Toggle patches in Halo executable - /// - /// HXE.Kernel.Configuration.Tweaks.Patches - /// Path to Halo executable - public void Write(uint cfg, string exePath) - { - var FilteredPatches = new List(); + /// + /// Toggle patches in Halo executable + /// + /// HXE.Kernel.Configuration.Tweaks.Patches + /// Path to Halo executable + public void Write(uint cfg, string exePath) + { + var FilteredPatches = new List(); - /** Configurable */ - bool DRM = (cfg & EXEP.DISABLE_DRM_AND_KEY_CHECKS) != 0; - bool NoGamma = (cfg & EXEP.DISABLE_SYSTEM_GAMMA) != 0; - bool NoAutoCenter = (cfg & EXEP.DISABLE_VEHICLE_AUTOCENTER) != 0; - bool BlockCamShake = (cfg & EXEP.BLOCK_CAMERA_SHAKE) != 0; - bool BlockDescopeOnDMG = (cfg & EXEP.PREVENT_DESCOPING_ON_DMG) != 0; + /** Configurable */ + bool DRM = (cfg & EXEP.DISABLE_DRM_AND_KEY_CHECKS) != 0; + bool NoGamma = (cfg & EXEP.DISABLE_SYSTEM_GAMMA) != 0; + bool NoAutoCenter = (cfg & EXEP.DISABLE_VEHICLE_AUTOCENTER) != 0; + bool BlockCamShake = (cfg & EXEP.BLOCK_CAMERA_SHAKE) != 0; + bool BlockDescopeOnDMG = (cfg & EXEP.PREVENT_DESCOPING_ON_DMG) != 0; - /** Overrides */ - bool LAA = true; // (cfg & EXEP.ENABLE_LARGE_ADDRESS_AWARE) != 0; - bool FixLAN = true; // (cfg & EXEP.BIND_SERVER_TO_0000) != 0; - bool Fix32Tex = true; // (cfg & EXEP.FIX_32BIT_TEXTURES) != 0; - bool NoMouseAccel = false; // (cfg & EXEP.DISABLE_MOUSE_ACCELERATION) != 0; - bool NoSafe = true; // (cfg & EXEP.FIX_32BIT_TEXTURES) != 0; - bool NoEULA = true; // (cfg & EXEP.DISABLE_EULA) != 0; - bool NoRegistryExit = true; // (cfg & EXEP.DISABLE_REG_EXIT_STATE) != 0; - bool BlockUpdates = true; // (cfg & EXEP.BLOCK_UPDATE_CHECKS) != 0; + /** Overrides */ + bool LAA = true; // (cfg & EXEP.ENABLE_LARGE_ADDRESS_AWARE) != 0; + bool FixLAN = true; // (cfg & EXEP.BIND_SERVER_TO_0000) != 0; + bool Fix32Tex = true; // (cfg & EXEP.FIX_32BIT_TEXTURES) != 0; + bool NoMouseAccel = false; // (cfg & EXEP.DISABLE_MOUSE_ACCELERATION) != 0; + bool NoSafe = true; // (cfg & EXEP.FIX_32BIT_TEXTURES) != 0; + bool NoEULA = true; // (cfg & EXEP.DISABLE_EULA) != 0; + bool NoRegistryExit = true; // (cfg & EXEP.DISABLE_REG_EXIT_STATE) != 0; + bool BlockUpdates = true; // (cfg & EXEP.BLOCK_UPDATE_CHECKS) != 0; - /** Filter PatchGroups for those requested - * NOTE: Update String matches as needed. - */ - foreach (var pg in Patches) - { - if (pg.Executable == "haloce.exe" && pg.Name.Contains("large address aware")) - { - pg.Toggle = LAA; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("DRM")) - { - pg.Toggle = DRM; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("Bind server to 0.0.0.0")) - { - pg.Toggle = FixLAN; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("safe mode prompt")) - { - pg.Toggle = NoSafe; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("gamma")) - { - pg.Toggle = NoGamma; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("32-bit textures")) - { - pg.Toggle = Fix32Tex; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("EULA")) - { - pg.Toggle = NoEULA; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("exit status")) - { - pg.Toggle = NoRegistryExit; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("auto-centering")) - { - pg.Toggle = NoAutoCenter; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("mouse acceleration")) - { + /** Filter PatchGroups for those requested + * NOTE: Update String matches as needed. + */ + foreach (var pg in Patches) + { + if (pg.Executable == "haloce.exe" && pg.Name.Contains("large address aware")) + { + pg.Toggle = LAA; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("DRM")) + { + pg.Toggle = DRM; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("Bind server to 0.0.0.0")) + { + pg.Toggle = FixLAN; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("safe mode prompt")) + { + pg.Toggle = NoSafe; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("gamma")) + { + pg.Toggle = NoGamma; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("32-bit textures")) + { + pg.Toggle = Fix32Tex; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("EULA")) + { + pg.Toggle = NoEULA; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("exit status")) + { + pg.Toggle = NoRegistryExit; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("auto-centering")) + { + pg.Toggle = NoAutoCenter; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("mouse acceleration")) + { - pg.Toggle = NoMouseAccel; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("update checks")) - { - pg.Toggle = BlockUpdates; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("camera shaking")) - { - pg.Toggle = BlockCamShake; - FilteredPatches.Add(pg); - continue; - } - if (pg.Executable == "haloce.exe" && pg.Name.Contains("Prevent descoping when taking damage")) - { - pg.Toggle = BlockDescopeOnDMG; - FilteredPatches.Add(pg); - continue; - } - } + pg.Toggle = NoMouseAccel; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("update checks")) + { + pg.Toggle = BlockUpdates; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("camera shaking")) + { + pg.Toggle = BlockCamShake; + FilteredPatches.Add(pg); + continue; + } + if (pg.Executable == "haloce.exe" && pg.Name.Contains("Prevent descoping when taking damage")) + { + pg.Toggle = BlockDescopeOnDMG; + FilteredPatches.Add(pg); + continue; + } + } - /** Flexible patcher */ - { - bool isFileReady = false; - while (!isFileReady) - { - try - { - using (var fs = new FileStream(exePath, FileMode.Open, FileAccess.ReadWrite)) - using (var ms = new MemoryStream(0x24B000)) - using (var bw = new BinaryWriter(ms)) - using (var br = new BinaryReader(ms)) - foreach (var PatchGroup in FilteredPatches) - { - foreach (var DataSet in PatchGroup.DataSets) + /** Flexible patcher */ + { + bool isFileReady = false; + while (!isFileReady) { - byte value = PatchGroup.Toggle ? DataSet.Patch : DataSet.Original; - long offset = DataSet.Offset; - ms.Position = 0; - fs.Position = 0; - fs.CopyTo(ms); + try + { + using (var fs = new FileStream(exePath, FileMode.Open, FileAccess.ReadWrite)) + using (var ms = new MemoryStream(0x24B000)) + using (var bw = new BinaryWriter(ms)) + using (var br = new BinaryReader(ms)) + foreach (var PatchGroup in FilteredPatches) + { + foreach (var DataSet in PatchGroup.DataSets) + { + byte value = PatchGroup.Toggle ? DataSet.Patch : DataSet.Original; + long offset = DataSet.Offset; + ms.Position = 0; + fs.Position = 0; + fs.CopyTo(ms); - ms.Position = offset; + ms.Position = offset; - if (br.ReadByte() != value) - { - if (PatchGroup.Name.Contains("DRM") && PatchGroup.Toggle == false) - return; + if (br.ReadByte() != value) + { + if (PatchGroup.Name.Contains("DRM") && PatchGroup.Toggle == false) + return; - ms.Position -= 1; /** restore position */ - bw.Write(value); /** write value */ + ms.Position -= 1; /** restore position */ + bw.Write(value); /** write value */ - fs.Position = 0; - ms.Position = 0; - ms.CopyTo(fs); + fs.Position = 0; + ms.Position = 0; + ms.CopyTo(fs); - if (PatchGroup.Toggle) - { - Info($"Applied \"{PatchGroup.Name}\" patch to the HCE executable"); + if (PatchGroup.Toggle) + { + Info($"Applied \"{PatchGroup.Name}\" patch to the HCE executable"); + } + else + { + Info($"Removed \"{PatchGroup.Name}\" patch from HCE executable and restored original values."); + } + } + else + Info($"HCE executable already patched with \"{PatchGroup.Name}\""); + } + } + isFileReady = true; } - else + catch (IOException) { - Info($"Removed \"{PatchGroup.Name}\" patch from HCE executable and restored original values."); + Wait("Waiting for Halo executable to be available for modification..."); + System.Threading.Thread.Sleep(1000); } - } - else - Info($"HCE executable already patched with \"{PatchGroup.Name}\""); } - } - isFileReady = true; - } - catch (IOException) - { - Wait("Waiting for Halo executable to be available for modification..."); - System.Threading.Thread.Sleep(1000); - } + } } - } - } - /// - /// Offsets for bitwise operations - /// - public static class EXEP - { - public const uint ENABLE_LARGE_ADDRESS_AWARE = 1 << 0x00; // Increase max memory range from 2GiB to 4GiB. - public const uint DISABLE_DRM_AND_KEY_CHECKS = 1 << 0x01; // Removes several DRM/key checks. - public const uint BIND_SERVER_TO_0000 = 1 << 0x02; // Fixes LAN game discovery. Helps systems with multiple interfaces. Can still bind to IP using `-ip` flag. - public const uint DISABLE_SAFEMODE_PROMPT = 1 << 0x03; // Disables prompt to restart Halo with disfunctional Safe Mode. - public const uint DISABLE_SYSTEM_GAMMA = 1 << 0x04; // Disables system-wide modification of display gamma. Disables in-game gamma altogether. Removes need for RegKeys. - public const uint FIX_32BIT_TEXTURES = 1 << 0x05; // Fixes 32-bit textures being truncated to only a blue channel. - public const uint DISABLE_EULA = 1 << 0x06; // Allows the game to be run without displaying the EULA. Removes the need for eula.dll to be present. - public const uint DISABLE_REG_EXIT_STATE = 1 << 0x07; // Makes the executable more portable by not requiring/adding registry keys. - public const uint DISABLE_VEHICLE_AUTOCENTER = 1 << 0x08; // In stock Halo, the crosshair in vehicles (e.g. scorpion tank) will slowly move towards the horizon as you move. - public const uint DISABLE_MOUSE_ACCELERATION = 1 << 0x09; // Self-explanatory. - public const uint BLOCK_UPDATE_CHECKS = 1 << 0x10; // Prevents checking for game updates. - public const uint BLOCK_CAMERA_SHAKE = 1 << 0x11; // Completely disable camera shake effect. Expose option to players prone to motion sickness. - public const uint PREVENT_DESCOPING_ON_DMG = 1 << 0x12; // Prevents zoomed-in weapons from descoping when the player takes damage. - //public const uint ADD_TAG = 1 << 0x13; // pR0Ps' signature + /// + /// Offsets for bitwise operations + /// + public static class EXEP + { + public const uint ENABLE_LARGE_ADDRESS_AWARE = 1 << 0x00; // Increase max memory range from 2GiB to 4GiB. + public const uint DISABLE_DRM_AND_KEY_CHECKS = 1 << 0x01; // Removes several DRM/key checks. + public const uint BIND_SERVER_TO_0000 = 1 << 0x02; // Fixes LAN game discovery. Helps systems with multiple interfaces. Can still bind to IP using `-ip` flag. + public const uint DISABLE_SAFEMODE_PROMPT = 1 << 0x03; // Disables prompt to restart Halo with disfunctional Safe Mode. + public const uint DISABLE_SYSTEM_GAMMA = 1 << 0x04; // Disables system-wide modification of display gamma. Disables in-game gamma altogether. Removes need for RegKeys. + public const uint FIX_32BIT_TEXTURES = 1 << 0x05; // Fixes 32-bit textures being truncated to only a blue channel. + public const uint DISABLE_EULA = 1 << 0x06; // Allows the game to be run without displaying the EULA. Removes the need for eula.dll to be present. + public const uint DISABLE_REG_EXIT_STATE = 1 << 0x07; // Makes the executable more portable by not requiring/adding registry keys. + public const uint DISABLE_VEHICLE_AUTOCENTER = 1 << 0x08; // In stock Halo, the crosshair in vehicles (e.g. scorpion tank) will slowly move towards the horizon as you move. + public const uint DISABLE_MOUSE_ACCELERATION = 1 << 0x09; // Self-explanatory. + public const uint BLOCK_UPDATE_CHECKS = 1 << 0x10; // Prevents checking for game updates. + public const uint BLOCK_CAMERA_SHAKE = 1 << 0x11; // Completely disable camera shake effect. Expose option to players prone to motion sickness. + public const uint PREVENT_DESCOPING_ON_DMG = 1 << 0x12; // Prevents zoomed-in weapons from descoping when the player takes damage. + //public const uint ADD_TAG = 1 << 0x13; // pR0Ps' signature + } } - } } From 90d40887aab48307ec8231e11aa3036de8155f58 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 20 Feb 2022 23:56:08 -0800 Subject: [PATCH 071/276] docs: add summary to PatchGroup --- src/Patcher.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index 0efea713..43343b45 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -8,11 +8,14 @@ namespace HXE { public class Patcher { - /// Read patches.crk to Patches list - /// foreach containing exe:haloce.exe, get and apply requested patches. - /// if a patch is no longer requested (enabled), restore the original value. - /// Patches will be passed to and stored in Kernel via bit-wise integer. - /// Later, some patches may be configured in SPV3 loader. Perhaps via The "Advanced" menu aka HXE's Configuration UserControl. Bring it full circle. + /// + /// Read patches.crk to Patches list
+ /// foreach containing exe:haloce.exe, get and apply requested patches.
+ /// if a patch is no longer requested (enabled), restore the original value.
+ /// Patches will be passed to and stored in Kernel via bit-wise integer.
+ /// Later, some patches may be configured in SPV3 loader.
+ /// Perhaps via The "Advanced" menu aka HXE's Configuration UserControl. Bring it full circle.
+ ///
public class PatchGroup { public string Name { get; set; } = string.Empty; /** Make large address aware */ From fc1b2f710ec6d37e3ca649041b89b05cfb2f6487 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:02:10 -0800 Subject: [PATCH 072/276] refactor: set List Patches to private --- src/Patcher.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index 43343b45..b11aed16 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -34,13 +34,13 @@ public class DataSet // 00000136: 0F 2F /// /// The list of patch groups specified by pR0ps' halo ce patches. /// - public static List Patches = Reader(); // See Write() for tmp overrides + private static readonly List _patches = Reader(); // See Write() for tmp overrides /// /// Reads PatchGroups from patches.crk to this instance. /// /// A list of PatchGroups read from the patches.crk file resource. - /// This is only used for Patches list initialization. + /// This is only used for _patches list initialization. public static List Reader() { /* Get patches.crk resource from assembly resources */ @@ -120,7 +120,7 @@ public static List Reader() } - /* Parse the data to members "PatchGroup" of list "Patches" */ + /* Parse the data to members "PatchGroup" of list "_patches" */ /* return list */ return list; @@ -156,7 +156,7 @@ public void Write(uint cfg, string exePath) /** Filter PatchGroups for those requested * NOTE: Update String matches as needed. */ - foreach (var pg in Patches) + foreach (var pg in _patches) { if (pg.Executable == "haloce.exe" && pg.Name.Contains("large address aware")) { From 5ca963f01b591b3b1a576725a3eba1ccd1535ba9 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:03:16 -0800 Subject: [PATCH 073/276] style: remove blank line --- src/Patcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index b11aed16..20b26139 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -214,7 +214,6 @@ public void Write(uint cfg, string exePath) } if (pg.Executable == "haloce.exe" && pg.Name.Contains("mouse acceleration")) { - pg.Toggle = NoMouseAccel; FilteredPatches.Add(pg); continue; From b54255fe6717995f25eb21e7b9a54574205c399e Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:04:32 -0800 Subject: [PATCH 074/276] style: add braces to using statement, if-else --- src/Patcher.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Patcher.cs b/src/Patcher.cs index 20b26139..db7827ae 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -249,6 +249,7 @@ public void Write(uint cfg, string exePath) using (var ms = new MemoryStream(0x24B000)) using (var bw = new BinaryWriter(ms)) using (var br = new BinaryReader(ms)) + { foreach (var PatchGroup in FilteredPatches) { foreach (var DataSet in PatchGroup.DataSets) @@ -283,9 +284,13 @@ public void Write(uint cfg, string exePath) } } else + { Info($"HCE executable already patched with \"{PatchGroup.Name}\""); + } } } + } + isFileReady = true; } catch (IOException) From 17bede8347cfa9465f30b85315ae5cf5f101b871 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:06:10 -0800 Subject: [PATCH 075/276] refactor: use decrement operator instead of -= --- src/Patcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index db7827ae..5e9db58e 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -267,7 +267,7 @@ public void Write(uint cfg, string exePath) if (PatchGroup.Name.Contains("DRM") && PatchGroup.Toggle == false) return; - ms.Position -= 1; /** restore position */ + ms.Position--; /** restore position */ bw.Write(value); /** write value */ fs.Position = 0; From 8b48c737e310537bc8d1be9388085b748084c7d8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:06:56 -0800 Subject: [PATCH 076/276] refactor: simplify boolean comparison --- src/Patcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index 5e9db58e..08d8843c 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -264,7 +264,7 @@ public void Write(uint cfg, string exePath) if (br.ReadByte() != value) { - if (PatchGroup.Name.Contains("DRM") && PatchGroup.Toggle == false) + if (PatchGroup.Name.Contains("DRM") && !PatchGroup.Toggle) return; ms.Position--; /** restore position */ From 84e438697449928e710696844044bec53306fcf1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:08:07 -0800 Subject: [PATCH 077/276] refactor: use explicit type instead of 'var' where type is not obvious --- src/Patcher.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index 08d8843c..e5c5e02f 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -156,7 +156,7 @@ public void Write(uint cfg, string exePath) /** Filter PatchGroups for those requested * NOTE: Update String matches as needed. */ - foreach (var pg in _patches) + foreach (PatchGroup pg in _patches) { if (pg.Executable == "haloce.exe" && pg.Name.Contains("large address aware")) { @@ -250,9 +250,9 @@ public void Write(uint cfg, string exePath) using (var bw = new BinaryWriter(ms)) using (var br = new BinaryReader(ms)) { - foreach (var PatchGroup in FilteredPatches) + foreach (PatchGroup PatchGroup in FilteredPatches) { - foreach (var DataSet in PatchGroup.DataSets) + foreach (DataSet DataSet in PatchGroup.DataSets) { byte value = PatchGroup.Toggle ? DataSet.Patch : DataSet.Original; long offset = DataSet.Offset; From 0618af27b5a6fc81b3a874cd0b32e42fdd40b05c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:08:43 -0800 Subject: [PATCH 078/276] chore(vscode): whitelist word "haloce" --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5a3b6411..b04d4441 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,5 +21,8 @@ "MD025": { "front_matter_title": "" } - } + }, + "cSpell.words": [ + "haloce" + ] } From da416a4c70c59fb040ebdc5314280c5dd27eccf0 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 00:12:44 -0800 Subject: [PATCH 079/276] refactor: use element access csharp(RCS1246) --- src/Patcher.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Patcher.cs b/src/Patcher.cs index e5c5e02f..1be6552f 100644 --- a/src/Patcher.cs +++ b/src/Patcher.cs @@ -79,7 +79,7 @@ public static List Reader() for (var index = 0; index < fileText.Count; index++) { /// If the first char in the line is a letter... */ - while (index < fileText.Count && char.IsLetter(fileText.ElementAt(index).ToCharArray().First())) + while (index < fileText.Count && char.IsLetter(fileText[index].ToCharArray().First())) { /** ...skip Name and Executable. Go to patch data. */ index += 2; @@ -87,24 +87,23 @@ public static List Reader() /** ...assign patch name, */ /** ...assign filename, */ /** ...and then read patch data. */ - while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) + while (index < fileText.Count && char.IsDigit(fileText[index].ToCharArray().First())) { patchGroup = new PatchGroup() { DataSets = new List(), - Name = fileText.ElementAt(index - 2), - Executable = fileText.ElementAt(index - 1) + Name = fileText[index - 2], + Executable = fileText[index - 1] }; - while (index < fileText.Count && char.IsDigit(fileText.ElementAt(index).ToCharArray().First())) + while (index < fileText.Count && char.IsDigit(fileText[index].ToCharArray().First())) { /** Read Patch Data to List, ... * Assign values{offset, original, patch} * proceed to next Patch Data, * then check if line is Patch Data */ - List values = fileText. - ElementAt(index).Split(byteSep, StringSplitOptions.RemoveEmptyEntries). + List values = fileText[index].Split(byteSep, StringSplitOptions.RemoveEmptyEntries). ToList(); patchGroup.DataSets.Add(new DataSet { From 747d3a9933d41e263d267f32e1b0a5d937647a27 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 01:08:54 -0800 Subject: [PATCH 080/276] ci(release): fix release workflow Remove TFM, RID --- .github/workflows/release.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5be04322..f26a4a7a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,13 +18,8 @@ jobs: name: Build runs-on: windows-latest env: - TFM: "net6.0-windows" ASSEMBLYNAME: "HXE" PROJPATH: "./src/HXE.csproj" - strategy: - fail-fast: false - matrix: - RID: [ "win-x86", "win-x64" ] steps: ################ @@ -56,13 +51,13 @@ jobs: # BUILD ################ - name: dotnet-publish - run: dotnet publish ${{ env.CSPROJ_RELPATH }} -c Release -t ${{ env.TFM }} --RID ${{ matrix.RID }} -p:ContinuousIntegrationBuild=true + run: dotnet publish ${{ env.CSPROJ_RELPATH }} -c Release -p:ContinuousIntegrationBuild=true # required variables: TargetFramework, RuntimeIdentifier, GitVersion_FullSemVer - name: Compress-PublishArtifacts run: | - $publishPath = Resolve-Path ".\bin\Release\$env:TFM\$env:RID\publish"; - $archiveName = "$env:ASSEMBLYNAME.$env:TFM-$env:RID.$env:GitVersion_FullSemVer.zip"; + $publishPath = Resolve-Path ".\bin\Release\**\publish"; + $archiveName = "$env:ASSEMBLYNAME.$env:GitVersion_FullSemVer.zip"; Set-Location $publishPath; Compress-Archive -Path $publishPath -DestinationPath ${{ github.workspace }}\bin\Release\publish\$archiveName -CompressionLevel Optimal; From 8455182f7c22d62ca149ffd568e2e55655f71e95 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 01:13:24 -0800 Subject: [PATCH 081/276] chore(deps): update namchee/conventional-pr action to v0.9.0 --- .github/workflows/conv-pull-requests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conv-pull-requests.yml b/.github/workflows/conv-pull-requests.yml index d2504e95..c9ab01f8 100644 --- a/.github/workflows/conv-pull-requests.yml +++ b/.github/workflows/conv-pull-requests.yml @@ -1,4 +1,4 @@ -name: Conventional Pull Request +name: Conventional Pull Request # See https://github.com/Namchee/conventional-pr on: From ce024c2f780241ca7026ff408391e22fce2abc08 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 01:15:24 -0800 Subject: [PATCH 082/276] ci(cpr): ignore snyk-bot style: set charset to UTF-8 BOM --- .github/workflows/conv-pull-requests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/conv-pull-requests.yml b/.github/workflows/conv-pull-requests.yml index c9ab01f8..970c1360 100644 --- a/.github/workflows/conv-pull-requests.yml +++ b/.github/workflows/conv-pull-requests.yml @@ -1,4 +1,4 @@ -name: Conventional Pull Request +name: Conventional Pull Request # See https://github.com/Namchee/conventional-pr on: @@ -16,3 +16,4 @@ jobs: uses: Namchee/conventional-pr@v0.12.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} + ignored_users: snyk-bot From 41da068d7db270b2a6db9967d3d25e57f35fedc8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 01:17:12 -0800 Subject: [PATCH 083/276] chore: remove CodeMaid config --- CodeMaid.config | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 CodeMaid.config diff --git a/CodeMaid.config b/CodeMaid.config deleted file mode 100644 index 37616a80..00000000 --- a/CodeMaid.config +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
- - - - - - False - - - 100 - - - False - - - True - - - - \ No newline at end of file From 6a24035ec119d9d69b3bb69bdd7f343a0f3d450e Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Feb 2022 01:35:14 -0800 Subject: [PATCH 084/276] docs: update SCREENSHOT.png --- SCREENSHOT.png | Bin 64562 -> 107796 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/SCREENSHOT.png b/SCREENSHOT.png index c51e3d3f4a8d529e3e0e285c70a529b6c8530933..a9a692635506ca951cda65945600ca69e3a5d846 100644 GIT binary patch literal 107796 zcmb@u1yogS*EWjsD2jxDlpqMwNK2PUcXw<;x)h{Sk?xWPX*RGyT3RI~r6sp?H*8`P z=id4}=e*DPzVH0!ALC~@0N1_Nz3!UVyyi9MCR9a98XNN=CK?(VwycbV8XDSPbZBU| zX71bsS6ao^pb`M$Bmp}WvV>qZAwh4I&ST$Tf`-k_m<@y>0$%Y)VS!=*VduYGYp zAt6D( z#anOOJ-50#!5zJdJ8%4Wcv#I==P_uo(CRiJ>BJD(yWxOqrUl+Fk`*0;j)rF38AuKW z6)B$`!9Km-P-gv56$1<=;vRnpFMCoB&I=qgG=EZJC#zJc8zv6M-k>j3qs03dbZ049 z*}Ki;pmQ$PCGZ{1ztmd4NTe0`E!VL6W)hhbz+0j8S8!-#n;^HE>f*gwWebe>G z?PqQjc%eaa%JrAxTZ!RUo7e9pmp%+a_S>$H$80BjdqN~5t!RiM+cicx@Z8(K?JAx4 zmO)y5-QC?U&yPKxy8M`)78DYinVwENxZ2x$B`w|cYz_jI_=muhwicW>+8zON>yg$CGun!-s

&R3|x)^fT) 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 Bo%jF% literal 64562 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 From 7a6ba1ceaf9ed3ef361f4d7240e209a6756a2764 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 14 Mar 2022 03:41:19 -0700 Subject: [PATCH 085/276] ci(cpr): rename step 'hqprs' to 'cpr' --- .github/workflows/conv-pull-requests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conv-pull-requests.yml b/.github/workflows/conv-pull-requests.yml index 970c1360..78ffceee 100644 --- a/.github/workflows/conv-pull-requests.yml +++ b/.github/workflows/conv-pull-requests.yml @@ -9,7 +9,7 @@ permissions: pull-requests: write jobs: - hqprs: + cpr: runs-on: ubuntu-latest steps: - name: conventional-pr From 231d1b96b989ac9959007a4f2e2238cd1abefd60 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 14 Mar 2022 03:43:02 -0700 Subject: [PATCH 086/276] chore(vscode): update Launch configuration --- .vscode/launch.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bc80cfab..ad85a9e0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,8 +9,9 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/bin/Debug/win-x64/HXE.dll", - "args": ["--test"], + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder/bin/Debug/net462/HXE.dll", + "args": [], "cwd": "${workspaceFolder}/src", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console": "internalConsole", From a52b37d05d62fc3c757db929769405e7a0bfe9bd Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 14 Mar 2022 03:44:23 -0700 Subject: [PATCH 087/276] chore(vscode): set OmniSharp settings useModernNet : true enableRoslynAnalyzers : true enableImportCompletion : true organizeImportsOnFormat : true --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b04d4441..694612ea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,5 +24,9 @@ }, "cSpell.words": [ "haloce" - ] + ], + "omnisharp.useModernNet": true, + "omnisharp.enableRoslynAnalyzers": true, + "omnisharp.enableImportCompletion": true, + "omnisharp.organizeImportsOnFormat": true } From 336434be68f2113e04075170a7302370f6f1d36b Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 14 Mar 2022 03:45:39 -0700 Subject: [PATCH 088/276] chore(vscode): update watch/run task --- .vscode/tasks.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 84bdcce5..8e624c5e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -32,9 +32,8 @@ "args": [ "watch", "run", - "${workspaceFolder}/src/HXE.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" + "--project", + "${workspaceFolder}/src/HXE.csproj" ], "problemMatcher": "$msCompile" } From 338830dbeba389994c7b6729a4994529139deb3d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Tue, 15 Mar 2022 00:32:25 -0700 Subject: [PATCH 089/276] ci(release): fix build/publish step --- .github/workflows/release.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f26a4a7a..01772927 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,7 @@ name: release # TODO -# - Get AssemblyName, TargetFramework (TFM), RuntimeIdentifier (RID) from Directory.Build.props, *.csproj -# - Overwrite `$env:RID and `$env:TFM with build props, allowing workflows to be driven by the project(s) -# - support multiple ASSEMBLYNAME parameters for matrices +# allow workflows to be driven by the project(s) on: push: @@ -51,9 +49,9 @@ jobs: # BUILD ################ - name: dotnet-publish - run: dotnet publish ${{ env.CSPROJ_RELPATH }} -c Release -p:ContinuousIntegrationBuild=true + run: dotnet publish ${{ env.PROJPATH }} -c Release -p:ContinuousIntegrationBuild=true - # required variables: TargetFramework, RuntimeIdentifier, GitVersion_FullSemVer + # required variables: GitVersion_FullSemVer - name: Compress-PublishArtifacts run: | $publishPath = Resolve-Path ".\bin\Release\**\publish"; From 6cefa56b184a27f78ede1370068447ab26f852ba Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Tue, 15 Mar 2022 01:09:01 -0700 Subject: [PATCH 090/276] docs: remove .NET 6 dependency notes We are using .NET Framework 4.6.2 --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 6ca78ee6..d5c847df 100644 --- a/README.md +++ b/README.md @@ -56,17 +56,6 @@ The USAGE document goes into detail on how to use HXE. In a nutshell: | ---------------------------------------- | ----------- | Windows 7 SP1 32-bit (w/ addl. software) | Windows 10 64-bit -## .NET 6.0 - -Because HXE is built on the relatively new .NET 6, you may need to download the [.NET 6.0 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) for this app to work. Hopefully, this will be distributed via Windows Updates to Windows 10 and Windows 11 sooner rather than later. -For 64-bit PCs: [Dotnet Runtime (Desktop) 6.0.1 Windows x64 Installer](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.1-windows-x64-installer) -For 32-bit PCs: [Dotnet Runtime (Desktop) 6.0.1 Windows x86 Installer](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.1-windows-x86-installer) - -### Windows 7/8.1 - -Additional software dependencies to be installed for this .NET-based app to work on Windows 8.1 and Windows 7 -Read the [Microsoft docs](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net60#additional-deps) to learn what you need and how to get it. - ## Note: Upgrading To Windows 10 Using the [Windows Installation Media Creation Tool](https://www.microsoft.com/en-us/software-download/windows10?36261b60-2f68-4336-abe2-4b00f210b6aa=True), you can still upgrade to Windows 10 with your Windows 7/8/8.1 license. From 6c3b71877c30c858a76cb4411af050d20a55197c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Tue, 15 Mar 2022 01:10:04 -0700 Subject: [PATCH 091/276] chore: remove redundant Semantic Release config --- release.config.js | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 release.config.js diff --git a/release.config.js b/release.config.js deleted file mode 100644 index b6cb881c..00000000 --- a/release.config.js +++ /dev/null @@ -1,20 +0,0 @@ -// Setup Semantic Release plugins. -// See https://semantic-release.gitbook.io/semantic-release/extending/plugins-list for more plugins. -module.exports = { - plugins: [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - "@semantic-release/changelog", - // Disable npm publishing - [ - "@semantic-release/npm", - { - npmPublish: false, - } - ], - "@semantic-release/github", - // Uncomment this line if you're using GitLab - // "@semantic-release/gitlab", - "@semantic-release/git" - ] -} \ No newline at end of file From e847d9de319964500ebb9e4134ae056e6f71f44b Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Mar 2022 01:02:25 -0700 Subject: [PATCH 092/276] build: use compiler defaults for Configuration (Release/Debug) --- src/HXE.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 616f10e8..6b7f1c07 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -1,7 +1,6 @@ - Debug ..\bin\$(Configuration) AnyCPU false From 5247cf0661eb17998b269c2608f2b1881562ad47 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Mar 2022 01:03:02 -0700 Subject: [PATCH 093/276] build: change NuGet copyright --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 6b7f1c07..195b4628 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -20,7 +20,7 @@ HaloSPV3 github/HaloSPV3 - Copyright © 2019 Emilian Roman + Copyright © 2022 Noah Sherwin A HCE wrapper and SPV3 loader True false From 5f7f425b6f8b3529a80eeeea67b8d54929291031 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Mar 2022 01:03:51 -0700 Subject: [PATCH 094/276] build: update Build, Publish properties --- src/HXE.csproj | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 195b4628..dc4636e1 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -34,10 +34,12 @@ snupkg true - - false - win-x86 + + true + win7-x86 true + $([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '6.0.300')) + $(Win7SF) {ACAA5D9F-B23D-43E1-B2DF-8C03230975A1} Properties @@ -48,16 +50,6 @@ true en-US true - $([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '6.0.300')) - $(Win7SF) - - - x64 - false - - - x86 - true DEBUG;TRACE @@ -69,7 +61,7 @@ true true - true + true From 52d3fd65f53e53dfb8f5bd6eafe5418c02a82912 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Mar 2022 01:04:43 -0700 Subject: [PATCH 095/276] build: change default language from 'en-US' to 'en' --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index dc4636e1..2e891b36 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -48,7 +48,7 @@ true hxe true - en-US + en true From 3e769c36a2eb7fabedc6b50cc101a35b8217df8c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Mon, 21 Mar 2022 01:05:23 -0700 Subject: [PATCH 096/276] build: add Label to some ItemGroups --- src/HXE.csproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 2e891b36..694d6537 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -102,15 +102,13 @@ - - + .docs-misc\readme.md - - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 39daf34211d0a4b0318453cb2574a6f5e0531adb Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 28 May 2023 16:45:11 -0700 Subject: [PATCH 097/276] build: add missing closing-parenthesis --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 694d6537..20aff53e 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -61,7 +61,7 @@ true true - true + true From 7963d9c35ab03de0b5a87c263b6fa60e269eaeb4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 28 May 2023 17:01:25 -0700 Subject: [PATCH 098/276] build: raise minimum runtime to net6.0 This effectively nullifies "fix: support targeting NETFX 4.6.2, 4.8.0" BREAKING CHANGES - .NET Framework 4.6.2 is no longer supported - .NET Framework 4.8.0 is no longer supported --- src/HXE.csproj | 8 ++------ src/Program.cs | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 20aff53e..c3b2ace6 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -5,8 +5,8 @@ AnyCPU false Exe - net462 - net462;net480;net5.0-windows;net6.0-windows + net6.0-windows + net6.0-windows HXE.Program HXE HXE @@ -119,10 +119,6 @@ all - - - - diff --git a/src/Program.cs b/src/Program.cs index 5932e821..99ffd902 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -423,7 +423,7 @@ internal static int GetLongestStringLength(string[] strings) { foreach (string line in s.Split('\n')) { - lines.AddRange(line.Split('\r').Where(l => !string.IsNullOrWhiteSpace(l))); + lines.AddRange(line.Split('\r', StringSplitOptions.RemoveEmptyEntries)); } } From 1ef51c1b85b58717c4693f49e46a264fdbd32284 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 28 May 2023 17:01:48 -0700 Subject: [PATCH 099/276] style: remove/add whitespace --- src/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Program.cs b/src/Program.cs index 99ffd902..c6f52b41 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -142,7 +142,7 @@ private static void InvokeProgram(string[] args) var options = new OptionSet() .Add("help", "Displays commands list", s => help = s != null) /* hxe command */ - .Add("test", "Start a dry run of HXE to self-test", s => test =s != null) /* hxe command */ + .Add("test", "Start a dry run of HXE to self-test", s => test = s != null) /* hxe command */ .Add("config", "Opens configuration GUI", s => config = s != null) /* hxe command */ .Add("positions", "Opens positions GUI", s => positions = s != null) /* hxe command */ .Add("cli", "Enable CLI of Positions or Config", s => cli = s != null) /* hxe parameter */ @@ -308,7 +308,7 @@ private static void InvokeProgram(string[] args) " -- The working directory is " + CurrentDirectory + NewLine + " -- Error: " + NewLine + e.ToString(); - var log = (File) Paths.Exception; + var log = (File)Paths.Exception; log.AppendAllText(msg + NewLine); Error(msg); } @@ -374,7 +374,7 @@ void Run(Action action) catch (Exception e) { var msg = " -- EXEC.START HALTED\n Error: " + e.ToString() + "\n"; - var log = (File) Paths.Exception; + var log = (File)Paths.Exception; log.AppendAllText(msg); Error(msg); System.Console.Error.WriteLine("\n\n" + e.StackTrace); From 3559ecda72b8309a55c3eac9f039d2916c770ff8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Tue, 30 May 2023 00:45:35 -0700 Subject: [PATCH 100/276] build(deps): use recommended PackageReference for Microsoft.SourceLink.GitHub --- src/HXE.csproj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index c3b2ace6..8a3b98d7 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -113,11 +113,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - 1.1.1 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + From 025eeede33f9ff4551db8d2bc0cd030bb246d5f4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 3 Jun 2023 16:59:23 -0700 Subject: [PATCH 101/276] build: remove redundant XML tags, properties --- src/HXE.csproj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 8a3b98d7..5357a12b 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -1,5 +1,4 @@ - - + ..\bin\$(Configuration) AnyCPU @@ -15,7 +14,7 @@ true - OnBuildSuccess + OnBuildSuccess HaloSPV3 @@ -34,7 +33,7 @@ snupkg true - + true win7-x86 true From 46c07a5ec6c95120aa3ecd4ec9b387ec33dc6e85 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 3 Jun 2023 17:10:28 -0700 Subject: [PATCH 102/276] build: remove '.root' links node_modules was hanging vscode-solution-explorer despite a non-recursive pattern. --- src/HXE.csproj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 5357a12b..95690a92 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -63,12 +63,8 @@ true - - .root\%(Filename)%(Extension) - Never - - .root\.github\%(RecursiveDir)%(Filename)%(Extension) + .github\%(RecursiveDir)%(Filename)%(Extension) .docs\%(RecursiveDir)%(Filename)%(Extension) From 34f977f36cefda949e79f7a971b0e3c60c42dc09 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 3 Jun 2023 17:27:44 -0700 Subject: [PATCH 103/276] build: move NuGet properties to new group; add IconUrl --- src/HXE.csproj | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 95690a92..4a2ad598 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -16,21 +16,6 @@ OnBuildSuccess - - HaloSPV3 - github/HaloSPV3 - Copyright © 2022 Noah Sherwin - A HCE wrapper and SPV3 loader - True - false - Zlib - icon.png - https://github.com/HaloSPV3/HXE - readme.md - Halo - true - https://github.com/HaloSPV3/HXE - snupkg true @@ -61,6 +46,32 @@ true true true + + + + + + HaloSPV3 + github/HaloSPV3 + HXE + $(ProductName) + A HCE wrapper and SPV3 loader + Copyright © 2023 Noah Sherwin + Zlib + https://github.com/HaloSPV3/HXE + https://github.com/HaloSPV3/HXE/blob/main/src/Assets/icon.png + icon.png + Halo + readme.md + + false + https://github.com/HaloSPV3/HXE + git + true + true + snupkg + True + true From 0894c932faaa34181f2c857ab035eae0ed1789e4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 3 Jun 2023 23:40:17 -0700 Subject: [PATCH 104/276] build: replace WPF dependency with Avalonia --- src/HXE.csproj | 16 +++++++++++++++- src/app.manifest | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/app.manifest diff --git a/src/HXE.csproj b/src/HXE.csproj index 4a2ad598..e976299b 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -31,9 +31,12 @@ Halo SPV3 Team true hxe - true en true + enable + true + app.manifest + true DEBUG;TRACE @@ -114,7 +117,18 @@ .docs-misc\readme.md + + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/app.manifest b/src/app.manifest new file mode 100644 index 00000000..50094c1a --- /dev/null +++ b/src/app.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + From 76b813b2c2f40213716ca727d23ea13ba9abea8a Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 00:41:11 -0700 Subject: [PATCH 105/276] feat: add HXE.Paths.Custom.Configuration(string) --- src/Paths.cs | 8 +++++++- src/Program.cs | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Paths.cs b/src/Paths.cs index b114cdee..2c9c3142 100644 --- a/src/Paths.cs +++ b/src/Paths.cs @@ -34,11 +34,12 @@ public static class Paths { public const string Executable = "hxe.exe"; public const string Manifest = "manifest.bin"; + public const string ConfigNameAndExt = "kernel-0x05.bin"; public static readonly string ProgFiles = GetFolderPath(ProgramFilesX86) ?? GetFolderPath(ProgramFiles); public static readonly string StartDirectory = Combine(GetDirectoryName(GetCurrentProcess().MainModule.FileName)); public static readonly string Directory = Combine(GetFolderPath(ApplicationData), "HXE"); - public static readonly string Configuration = Combine(Directory, "kernel-0x05.bin"); + public static readonly string Configuration = Combine(Directory, ConfigNameAndExt); public static readonly string Exception = Combine(Directory, "exception.log"); public static readonly string Positions = Combine(CurrentDirectory, "positions.bin"); public static readonly string DSOAL = Combine(CurrentDirectory, "dsoal-aldrv.dll"); @@ -98,6 +99,11 @@ public static string Waypoint(string profile) public class Custom { + public static string Configuration(string directory) + { + return Combine(directory, ConfigNameAndExt); + } + public static string Profiles(string directory) { return Combine(directory, "savegames"); diff --git a/src/Program.cs b/src/Program.cs index c6f52b41..e32a9808 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -168,6 +168,7 @@ private static void InvokeProgram(string[] args) Info("Discovered CLI command: " + i); var hce = new Executable(); + Kernel.Configuration? configuration; if (help) { @@ -175,6 +176,12 @@ private static void InvokeProgram(string[] args) WithCode(Code.Success); } + if (!string.IsNullOrWhiteSpace(path)) + { + hce.Profile.Path = path; + configuration = new Kernel.Configuration(Paths.Custom.Configuration(path)); + } + if (test) { // TODO: Move to Test.cs; reduce Program.cs bloat @@ -331,9 +338,6 @@ private static void InvokeProgram(string[] args) if (!string.IsNullOrWhiteSpace(adapter)) hce.Video.Adapter = byte.Parse(adapter); - if (!string.IsNullOrWhiteSpace(path)) - hce.Profile.Path = path; - if (!string.IsNullOrWhiteSpace(exec)) hce.Debug.Initiation = exec; From e78b6b5c3fec3f8274068a2235e7ce3d497e0972 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 00:40:19 -0700 Subject: [PATCH 106/276] style: fix indenting --- src/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Program.cs b/src/Program.cs index e32a9808..d3ffdbc7 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -142,7 +142,7 @@ private static void InvokeProgram(string[] args) var options = new OptionSet() .Add("help", "Displays commands list", s => help = s != null) /* hxe command */ - .Add("test", "Start a dry run of HXE to self-test", s => test = s != null) /* hxe command */ + .Add("test", "Start a dry run of HXE to self-test", s => test = s != null) /* hxe command */ .Add("config", "Opens configuration GUI", s => config = s != null) /* hxe command */ .Add("positions", "Opens positions GUI", s => positions = s != null) /* hxe command */ .Add("cli", "Enable CLI of Positions or Config", s => cli = s != null) /* hxe parameter */ From 61260add8304d79b2592c6ec89a0e05dbee04f82 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 00:43:45 -0700 Subject: [PATCH 107/276] style: remove unnecessary blank lines --- src/Program.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Program.cs b/src/Program.cs index d3ffdbc7..2c679628 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -398,7 +398,6 @@ private static void DisplayBanner() string.Format(BannerBuildSourceRelease, GitVersionInformation.MajorMinorPatch) : string.Format(BannerBuildSourceCommit, GitVersionInformation.ShortSha); - int longestStringLength = GetLongestStringLength(new string[]{ Banner, string.Format(BannerBuildNumber, infoVersion), @@ -434,7 +433,6 @@ internal static int GetLongestStringLength(string[] strings) /// Get the length of the longest line foreach (string line in lines) { - if (line.Length > longestStringLength) { longestStringLength = line.Length; From d4b5be11597d5602c96569aadec0d4cfc85b2d03 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 00:44:48 -0700 Subject: [PATCH 108/276] style: use expression-bodied lambda --- src/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Program.cs b/src/Program.cs index 2c679628..2b937fb6 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -362,7 +362,7 @@ private static void InvokeProgram(string[] args) * Implicitly invoke the HXE kernel with the HCE loading procedure. */ - Run(() => { Kernel.Invoke(hce); }); + Run(() => Kernel.Invoke(hce)); /** * This method is used for running code asynchronously and catching exceptions at the highest level. From ac6b442b0a505ce82c076ae70c42fa9c111fe538 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:22:35 -0700 Subject: [PATCH 109/276] refactor: use Environment.ProcessPath --- src/Paths.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Paths.cs b/src/Paths.cs index 2c9c3142..ca89c3fa 100644 --- a/src/Paths.cs +++ b/src/Paths.cs @@ -19,7 +19,6 @@ * 3. This notice may not be removed or altered from any source distribution. */ -using static System.Diagnostics.Process; using static System.Environment; using static System.Environment.SpecialFolder; using static System.IO.File; @@ -37,7 +36,7 @@ public static class Paths public const string ConfigNameAndExt = "kernel-0x05.bin"; public static readonly string ProgFiles = GetFolderPath(ProgramFilesX86) ?? GetFolderPath(ProgramFiles); - public static readonly string StartDirectory = Combine(GetDirectoryName(GetCurrentProcess().MainModule.FileName)); + public static readonly string StartDirectory = Combine(GetDirectoryName(ProcessPath)); public static readonly string Directory = Combine(GetFolderPath(ApplicationData), "HXE"); public static readonly string Configuration = Combine(Directory, ConfigNameAndExt); public static readonly string Exception = Combine(Directory, "exception.log"); From dfd4c3645f2d579176b29e8a8e64c35b33a1980f Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:22:51 -0700 Subject: [PATCH 110/276] style: add eof newline --- src/Paths.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Paths.cs b/src/Paths.cs index ca89c3fa..9a4c2090 100644 --- a/src/Paths.cs +++ b/src/Paths.cs @@ -197,4 +197,4 @@ public static void SetSteam(string steamexepath) } } } -} \ No newline at end of file +} From 6ff8b064c2287b05c94de932d31df8570fe7f5c0 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:23:36 -0700 Subject: [PATCH 111/276] docs: update HXE.Paths.Steam comments --- src/Paths.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Paths.cs b/src/Paths.cs index 9a4c2090..17bd5ca9 100644 --- a/src/Paths.cs +++ b/src/Paths.cs @@ -185,9 +185,11 @@ public class Steam public static readonly string SteamDefault = Combine(ProgFiles, "Steam"); - public static string Directory = SteamDefault; /// Change via SetSteam(steamexepath) + ///

Change via SetSteam(steamexepath) + public static string Directory = SteamDefault; public static string Libraries = Combine(Directory, "steamapps", "libraryfolders.vdf"); - public static string Library = Directory; /// Change directly or by assigning an element from Libraries.LibList[] + /// Change directly or by assigning an element from + public static string Library = Directory; public static void SetSteam(string steamexepath) { From d9d5ee5c0599155f2f21111a16693e6ad5b465a4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:38:48 -0700 Subject: [PATCH 112/276] feat: add currently unused MainWindow --- src/MainWindow.axaml | 9 +++++++++ src/MainWindow.axaml.cs | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/MainWindow.axaml create mode 100644 src/MainWindow.axaml.cs diff --git a/src/MainWindow.axaml b/src/MainWindow.axaml new file mode 100644 index 00000000..8ef2ab4b --- /dev/null +++ b/src/MainWindow.axaml @@ -0,0 +1,9 @@ + + Welcome to Avalonia! + diff --git a/src/MainWindow.axaml.cs b/src/MainWindow.axaml.cs new file mode 100644 index 00000000..21922c12 --- /dev/null +++ b/src/MainWindow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace HXE; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} From 64b1dcadfb71abec0907e7c43b5c14e9b02026b1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:39:32 -0700 Subject: [PATCH 113/276] feat: add 'App', an Application just for MainWindow --- src/App.axaml | 10 ++++++++++ src/App.axaml.cs | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/App.axaml create mode 100644 src/App.axaml.cs diff --git a/src/App.axaml b/src/App.axaml new file mode 100644 index 00000000..aa620003 --- /dev/null +++ b/src/App.axaml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/src/App.axaml.cs b/src/App.axaml.cs new file mode 100644 index 00000000..ad46cea2 --- /dev/null +++ b/src/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace HXE; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} From b024bb80b7da6b96579c82f3d47e8be4ac530408 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:40:29 -0700 Subject: [PATCH 114/276] build: comment out TargetFrameworks Avalonia 11.0.0-Preview8 and rc1.1 do not like it. A fix was merged. --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index e976299b..bccd7c3e 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -5,7 +5,7 @@ false Exe net6.0-windows - net6.0-windows + HXE.Program HXE HXE From f90f752e8946595305469072f9109b9eb53b8d8c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:42:02 -0700 Subject: [PATCH 115/276] style: remove spaces; add eol newline --- src/OpenSauce.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenSauce.cs b/src/OpenSauce.cs index 66750eba..4de3036c 100644 --- a/src/OpenSauce.cs +++ b/src/OpenSauce.cs @@ -64,7 +64,7 @@ public void Load() using (var reader = new StringReader(ReadAllText())) { var serialiser = new XmlSerializer(typeof(OpenSauce)); - var serialised = (OpenSauce) serialiser.Deserialize(reader); + var serialised = (OpenSauce)serialiser.Deserialize(reader); CacheFiles = serialised.CacheFiles; Rasterizer = serialised.Rasterizer; @@ -260,7 +260,7 @@ public class NetworkingVersionCheck public class VersionCheckDate { - public int Day { get; set; } = (int) DateTime.Now.DayOfWeek; + public int Day { get; set; } = (int)DateTime.Now.DayOfWeek; public int Month { get; set; } = DateTime.Now.Month; public int Year { get; set; } = DateTime.Now.Year; } @@ -315,7 +315,7 @@ public void Load(string path) using (var reader = new StringReader(Unicode.GetString(inflatedStream.ToArray()))) { - Positions = (List) new XmlSerializer(typeof(List)).Deserialize(reader); + Positions = (List)new XmlSerializer(typeof(List)).Deserialize(reader); } } } @@ -373,4 +373,4 @@ public class HUDHUDScale } } } -} \ No newline at end of file +} From ffd1ff13d7eba1f6764e981e7aafabb157d4d289 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 4 Jun 2023 01:47:37 -0700 Subject: [PATCH 116/276] feat: migrate HXE.Positions to Avalonia --- src/Positions.axaml | 108 ++++++++++++++++++++++++ src/Positions.axaml.cs | 181 +++++++++++++++++++++++++++++++++++++++++ src/Positions.xaml | 78 ------------------ src/Positions.xaml.cs | 97 ---------------------- 4 files changed, 289 insertions(+), 175 deletions(-) create mode 100644 src/Positions.axaml create mode 100644 src/Positions.axaml.cs delete mode 100644 src/Positions.xaml delete mode 100644 src/Positions.xaml.cs diff --git a/src/Positions.axaml b/src/Positions.axaml new file mode 100644 index 00000000..eaec975a --- /dev/null +++ b/src/Positions.axaml @@ -0,0 +1,108 @@ + + + + + + + +
public partial class Settings : Window { - private SettingsCore _core = new SettingsCore(new Kernel.Configuration(Paths.Configuration)); + private SettingsCore _core = SettingsCore.Current; public Kernel.Configuration Configuration { From 9bc6f49fec6a294e19f0c2a0dd1178dc76c1516e Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 2 Jul 2023 20:41:50 -0700 Subject: [PATCH 237/276] fix(gui): remove erroneous Configuration.Load() call This was overwriting any existing Configuration instance --- src/Settings.axaml.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Settings.axaml.cs b/src/Settings.axaml.cs index ddabf267..106b4866 100644 --- a/src/Settings.axaml.cs +++ b/src/Settings.axaml.cs @@ -69,8 +69,6 @@ public void Initialize() Console.Info("Loading kernel settings"); - Configuration.Load(); - try { AssignConfig(); } catch (Exception e) when (e.Message.Equals("Kernel Mode not recognized.")) { From ea06c8ba59963af063fcf0c7fc7b112f8d15da3a Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 2 Jul 2023 20:42:52 -0700 Subject: [PATCH 238/276] fix(gui): print Settings.Initialize() errors to console --- src/Settings.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Settings.axaml.cs b/src/Settings.axaml.cs index 106b4866..74a17c70 100644 --- a/src/Settings.axaml.cs +++ b/src/Settings.axaml.cs @@ -72,6 +72,7 @@ public void Initialize() try { AssignConfig(); } catch (Exception e) when (e.Message.Equals("Kernel Mode not recognized.")) { + Console.Error(e.ToString()); Configuration = new Kernel.Configuration(Configuration.Path); Configuration.Save(); AssignConfig(); From 96f3873040e0e847df7a8c81641022a6dac6472c Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 2 Jul 2023 20:43:24 -0700 Subject: [PATCH 239/276] fix(gui): add missing RectGeometryConverter --- src/GUI/Converters/RectGeometryConverter.cs | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/GUI/Converters/RectGeometryConverter.cs diff --git a/src/GUI/Converters/RectGeometryConverter.cs b/src/GUI/Converters/RectGeometryConverter.cs new file mode 100644 index 00000000..b275f830 --- /dev/null +++ b/src/GUI/Converters/RectGeometryConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace HXE.GUI.Converters; + +public class RectGeometryConverter : IValueConverter +{ + public static readonly RectGeometryConverter Instance = new(); + + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is Rect rect) + return new RectangleGeometry(rect); + + // converter used for the wrong type + return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); + } + + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is RectangleGeometry rectangle) + return rectangle.Rect; + + // converter used for the wrong type + return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); + } + +} From cb78ef91f0ba1f78b76b625ca60ecd86741e422a Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 2 Jul 2023 20:45:37 -0700 Subject: [PATCH 240/276] feat(gui): add buttons in MainWindow to open Settings, Positions --- src/MainWindow.axaml | 16 +++++++++-- src/MainWindow.axaml.cs | 64 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/MainWindow.axaml b/src/MainWindow.axaml index 8ef2ab4b..535de9c5 100644 --- a/src/MainWindow.axaml +++ b/src/MainWindow.axaml @@ -4,6 +4,18 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="HXE.MainWindow" - Title="HXE"> - Welcome to Avalonia! + Title="HXE" + SizeToContent="WidthAndHeight"> + + + + + + + Welcome to Avalonia! + + + + + diff --git a/src/MainWindow.axaml.cs b/src/MainWindow.axaml.cs index b8637bae..b1391787 100644 --- a/src/MainWindow.axaml.cs +++ b/src/MainWindow.axaml.cs @@ -1,8 +1,70 @@ +using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Interactivity; namespace HXE; public partial class MainWindow : Window { - public MainWindow() => InitializeComponent(); + public MainWindow() + { + InitializeComponent(); + } + + private Settings? _settings = null; + private Positions? _positions = null; + + public Settings Settings + { + get + { + if (_settings is null) + { + queryExistingWindows(); + if (_settings is null) + _settings = new(); + } + return _settings; + } + set => _settings = value; + } + public Positions Positions + { + get + { + if (_positions is null) + { + queryExistingWindows(); + if (_positions is null) + _positions = new(); + } + return _positions; + } + set => _positions = value; + } + + public void OpenSettings(object sender, RoutedEventArgs args) + { + Settings.Show(this); + } + + public void OpenPositions(object sender, RoutedEventArgs args) + { + Positions.Show(this); + } + + private void queryExistingWindows() + { + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopStyleApplicationLifetime) + { + foreach (var w in desktopStyleApplicationLifetime.Windows) + { + if (_settings is null && w is Settings settings) + _settings = settings; + else if (_positions is null && w is Positions positions) + _positions = positions; + } + } + } } From 9f3da6c5d14041bcb9eda24aeb897c2a95bb82e1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 15 Jul 2023 19:13:33 -0700 Subject: [PATCH 241/276] refactor: move Windows P/Invoke symbols to PlatformImpl internals --- src/Kernel.cs | 30 -------- src/OpenSauce.cs | 112 +--------------------------- src/PlatformImpl/Linux/PInvoke.cs | 99 ++++++++++++++++++++++++ src/PlatformImpl/Windows/PInvoke.cs | 62 +++++++++++++++ 4 files changed, 164 insertions(+), 139 deletions(-) create mode 100644 src/PlatformImpl/Linux/PInvoke.cs create mode 100644 src/PlatformImpl/Windows/PInvoke.cs diff --git a/src/Kernel.cs b/src/Kernel.cs index a88c2a88..a41b0029 100644 --- a/src/Kernel.cs +++ b/src/Kernel.cs @@ -44,36 +44,6 @@ namespace HXE ///
public static class Kernel { - [DllImport("USER32.DLL", EntryPoint = "SetWindowPos")] - public static extern IntPtr SetWindowPos - ( - IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags - ); - - [DllImport("USER32.DLL")] - private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - - [DllImport("USER32.DLL")] - private static extern int GetWindowLong(IntPtr hWnd, int nIndex); - - /// - [DllImport("USER32.DLL")] - internal static extern int GetSystemMetrics(int nIndex); - - /// The width of the screen of the primary display monitor, in pixels.This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps(hdcPrimaryMonitor, HORZRES). - internal const int SM_CXSCREEN = 0; - /// The height of the screen of the primary display monitor, in pixels. This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps( hdcPrimaryMonitor, VERTRES)./// - internal const int SM_CYSCREEN = 1; - - [DllImport("KERNEL32.DLL")] - public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); - - [DllImport("KERNEL32.DLL")] - public static extern bool ReadProcessMemory - ( - int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead - ); - /// /// Loads HCE executable in the working directory. /// diff --git a/src/OpenSauce.cs b/src/OpenSauce.cs index 51b691ed..45ad7475 100644 --- a/src/OpenSauce.cs +++ b/src/OpenSauce.cs @@ -23,9 +23,9 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; -using System.Threading; using System.Xml.Serialization; using Avalonia.Controls; +using HXE.PlatformImpl; using static System.IO.Compression.CompressionMode; using static System.Math; using static System.Text.Encoding; @@ -228,114 +228,8 @@ public class OpenSauceCamera /// The optimal Halo:CE FOV for the Primary (or first-discovered) display's current resolution. If the display resolution does not match the resolution you play with, this FOV will be incorrect. public double CalculateFOV(WindowBase? window = null) { - var (w, h) = (0.0, 0.0); - - if (window is not null && window.Screens.ScreenCount is not 0) - { - // I *could* use tuple assignment for style points, but it doesn't make these assignments any more succinct. - // If-Else results in one "is not null" comparison while conditional assignment expressions would result in two comparisons. - if (window.Screens.Primary is not null) - { - w = window.Screens.Primary.Bounds.Width; - h = window.Screens.Primary.Bounds.Height; - } - else - { - Console.Warn($"Primary display could not be determined. Defaulting to first monitor in index. Identify it by Bounds '{window.Screens.All[0].Bounds}'."); - w = window.Screens.All[0].Bounds.Width; - h = window.Screens.All[0].Bounds.Height; - } - } - else if (OperatingSystem.IsWindows()) - { - Console.Warn("Avalonia backend is not initialized or its windowing API failed to enumerate connected displays"); - w = Kernel.GetSystemMetrics(Kernel.SM_CXSCREEN); - h = Kernel.GetSystemMetrics(Kernel.SM_CYSCREEN); - } - else if (OperatingSystem.IsLinux()) // Developed on Windows. Tested on WSL Ubuntu. - { - string? data = null; - using System.Diagnostics.Process xrandr_primary = new() - { - StartInfo = new() - { - FileName = "sh", - // should print resolution (e.g. "1920x1080") of primary display if its connected - Arguments = "-c \"xrandr --current | grep 'connected primary' | grep --extended-regexp --only-matching '[1-9][0-9]+x[1-9][0-9]+' | head -1\"" // will match 10x10 or larger. first line of matches: 1920x1080 - } - }; - - xrandr_primary.OutputDataReceived += (sender, args) => data = args.Data; - xrandr_primary.Start(); - xrandr_primary.BeginOutputReadLine(); - - const uint timeout = 100; - for (uint msWaited = 0; data is null && msWaited < timeout; msWaited++) - Thread.Sleep(1); - - if (string.IsNullOrEmpty(data)) - { - xrandr_primary.Kill(true); - Console.Warn( - $"OS is '{Environment.OSVersion}'.\n" + - $"The shell or xrandr failed to respond within {timeout}ms\n" + - "-OR- xrandr was not found\n" + - "-OR- xrandr did not return data\n" + - "-OR- the Primary monitor is not connected\n" + - "-OR- none of the connected monitors are marked 'Primary'\n" + - "-OR- the Primary monitor's data was printed in a different format than XxY."); - - /** - https://askubuntu.com/a/1351112 - https://superuser.com/a/603618 - */ - using System.Diagnostics.Process xrandr_first = new() - { - StartInfo = new() - { - FileName = "sh", - Arguments = "-c \"xrandr --current | grep '*' | grep --extended-regexp --only-matching '[1-9][0-9]+x[1-9][0-9]+' | head -1\"", - CreateNoWindow = true, - RedirectStandardOutput = true, - } - }; - xrandr_first.OutputDataReceived += (sender, args) => data = args.Data; - xrandr_first.Start(); - xrandr_first.BeginOutputReadLine(); - - for (uint msWaited = 0; data is null && msWaited < timeout; msWaited++) - Thread.Sleep(1); - - xrandr_first.Kill(true); - - if (string.IsNullOrEmpty(data)) - { - throw new TimeoutException($"OS is '{Environment.OSVersion}' and sh/xrandr failed to respond within 100ms or failed to get the resolution of the first connected monitor."); - } - } - else if (data.Trim('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') is "x") - { - string[] xy = data.Split('x', StringSplitOptions.RemoveEmptyEntries); - Console.Info("Attempting to parse data for resolution width and height..."); - w = double.Parse(xy[0]); - h = double.Parse(xy[1]); - } - else - { - throw new InvalidOperationException($"grepped string '{data}' did not match expected pattern."); - } - } - else if (OperatingSystem.IsMacOS()) - { - throw new NotSupportedException("This API feature does not yet support Mac OS"); - } - - if (w is 0 || h is 0) - throw new InvalidOperationException("Unable to query the current resolution of any display/screen."); - - Console.Info($"Monitor resolution successfully acquired. ({w}x{h})"); - - return CalculateFOV(w, h); + (double width, double height) = VideoDisplay.GetResolution(window); + return CalculateFOV(width, height); } public double CalculateFOV(double width, double height) diff --git a/src/PlatformImpl/Linux/PInvoke.cs b/src/PlatformImpl/Linux/PInvoke.cs new file mode 100644 index 00000000..b1aed6c8 --- /dev/null +++ b/src/PlatformImpl/Linux/PInvoke.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Threading; + +namespace HXE.PlatformImpl.Linux; + +/// +/// +/// +/// References
+/// - https://carpenoctem.dev/blog/posix-signal-handling-in-dotnet-6/ +/// - https://www.nuget.org/packages/Mono.Posix +///
+ +internal static class PInvoke +{ + //[DllImport("libxrandr.so.2")] + //public static void + + public static (uint w, uint h) GetScreenResolutionFromXrandr() + { + string? data = null; + using System.Diagnostics.Process xrandr_primary = new() + { + StartInfo = new() + { + FileName = "sh", + // should print resolution (e.g. "1920x1080") of primary display if its connected + Arguments = "-c \"xrandr --current | grep 'connected primary' | grep --extended-regexp --only-matching '[1-9][0-9]+x[1-9][0-9]+' | head -1\"" // will match 10x10 or larger. first line of matches: 1920x1080 + } + }; + + xrandr_primary.OutputDataReceived += (sender, args) => data = args.Data; + xrandr_primary.Start(); + xrandr_primary.BeginOutputReadLine(); + + const uint timeout = 100; + for (uint msWaited = 0; data is null && msWaited < timeout; msWaited++) + Thread.Sleep(1); + + // could not get primary display's properties. Instead, use first monitor in list. + if (string.IsNullOrEmpty(data)) + { + xrandr_primary.Kill(true); + Console.Warn( + $"OS is '{Environment.OSVersion}'.\n" + + $"The shell or xrandr failed to respond within {timeout}ms\n" + + "-OR- xrandr was not found\n" + + "-OR- xrandr did not return data\n" + + "-OR- the Primary monitor is not connected\n" + + "-OR- none of the connected monitors are marked 'Primary'\n" + + "-OR- the Primary monitor's data was printed in a different format than XxY."); + + /** + https://askubuntu.com/a/1351112 + https://superuser.com/a/603618 + */ + using System.Diagnostics.Process xrandr_first = new() + { + StartInfo = new() + { + FileName = "sh", + Arguments = "-c \"xrandr --current | grep '*' | grep --extended-regexp --only-matching '[1-9][0-9]+x[1-9][0-9]+' | head -1\"", + CreateNoWindow = true, + RedirectStandardOutput = true, + } + }; + xrandr_first.OutputDataReceived += (sender, args) => data = args.Data; + xrandr_first.Start(); + xrandr_first.BeginOutputReadLine(); + // data should be something like "1920x1080" + + for (uint msWaited = 0; data is null && msWaited < timeout; msWaited++) + Thread.Sleep(1); + + xrandr_first.Kill(true); + + if (string.IsNullOrEmpty(data)) + { + throw new TimeoutException($"OS is '{Environment.OSVersion}' and sh/xrandr failed to respond within 100ms or failed to get the resolution of the first connected monitor."); + } + } + + // If data, without Width and Height, is "x", parse data for Width and Height. + if (data.Trim('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') is "x") + { + string[] xy = data.Split('x', StringSplitOptions.RemoveEmptyEntries); + Console.Info("Attempting to parse data for resolution width and height..."); + return (uint.Parse(xy[0]), uint.Parse(xy[1])); + } + else + { + throw new InvalidOperationException($"grepped string '{data}' did not match expected pattern."); + } + } +} diff --git a/src/PlatformImpl/Windows/PInvoke.cs b/src/PlatformImpl/Windows/PInvoke.cs new file mode 100644 index 00000000..09e151a9 --- /dev/null +++ b/src/PlatformImpl/Windows/PInvoke.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.InteropServices; + +namespace HXE.PlatformImpl.Windows; + +internal static class PInvoke +{ + /// The width of the screen of the primary display monitor, in pixels.This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps(hdcPrimaryMonitor, HORZRES). + internal const int SM_CXSCREEN = 0; + /// The height of the screen of the primary display monitor, in pixels. This is the same value obtained by calling GetDeviceCaps as follows: GetDeviceCaps( hdcPrimaryMonitor, VERTRES)./// + internal const int SM_CYSCREEN = 1; + + #region user32 + + [DllImport("USER32.DLL")] + internal static extern IntPtr SetWindowPos + ( + IntPtr hWnd, + int hWndInsertAfter, + int x, + int Y, + int cx, + int cy, + int wFlags + ); + + [DllImport("USER32.DLL")] + internal static extern int SetWindowLong( + IntPtr hWnd, + int nIndex, + int dwNewLong + ); + + [DllImport("USER32.DLL")] + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + /// + [DllImport("USER32.DLL")] + internal static extern int GetSystemMetrics(int nIndex); + + #endregion user32 + #region kernel32 + + [DllImport("KERNEL32.DLL")] + internal static extern IntPtr OpenProcess( + int dwDesiredAccess, + bool bInheritHandle, + int dwProcessId + ); + + [DllImport("KERNEL32.DLL")] + internal static extern bool ReadProcessMemory + ( + int hProcess, + int lpBaseAddress, + byte[] lpBuffer, + int dwSize, + ref int lpNumberOfBytesRead + ); + + #endregion +} From 48e99dfc16a7a6a5e8cbe7ac34792d14062f07bf Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 15 Jul 2023 19:20:52 -0700 Subject: [PATCH 242/276] style: remove unnecessary usings --- src/PlatformImpl/Linux/PInvoke.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PlatformImpl/Linux/PInvoke.cs b/src/PlatformImpl/Linux/PInvoke.cs index b1aed6c8..8ed95cad 100644 --- a/src/PlatformImpl/Linux/PInvoke.cs +++ b/src/PlatformImpl/Linux/PInvoke.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Runtime.InteropServices; using System.Threading; namespace HXE.PlatformImpl.Linux; @@ -17,8 +13,7 @@ namespace HXE.PlatformImpl.Linux; internal static class PInvoke { - //[DllImport("libxrandr.so.2")] - //public static void + // Screens can be acquired via xrandr (cli), libxrandr, or Wayland protocol public static (uint w, uint h) GetScreenResolutionFromXrandr() { From 3c509dc997d5c55a4a9f1b12d9f0d97c517dd5de Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sat, 15 Jul 2023 19:21:58 -0700 Subject: [PATCH 243/276] feat: add class VideoDisplay, method GetResolution --- src/PlatformImpl/VideoDisplay.cs | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/PlatformImpl/VideoDisplay.cs diff --git a/src/PlatformImpl/VideoDisplay.cs b/src/PlatformImpl/VideoDisplay.cs new file mode 100644 index 00000000..4b53973d --- /dev/null +++ b/src/PlatformImpl/VideoDisplay.cs @@ -0,0 +1,50 @@ +using System; +using Avalonia.Controls; + +namespace HXE.PlatformImpl; + +public class VideoDisplay +{ + public static (uint width, uint height) GetResolution(WindowBase? window = null) + { + var (w, h) = (0u, 0u); + + if (window is not null && window.Screens.ScreenCount is not 0) + { + // I *could* use tuple assignment for style points, but it doesn't make these assignments any more succinct. + // If-Else results in one "is not null" comparison while conditional assignment expressions would result in two comparisons. + if (window.Screens.Primary is not null) + { + w = (uint)window.Screens.Primary.Bounds.Width; + h = (uint)window.Screens.Primary.Bounds.Height; + } + else + { + Console.Warn($"Primary display could not be determined. Defaulting to first monitor in index. Identify it by Bounds '{window.Screens.All[0].Bounds}'."); + w = (uint)window.Screens.All[0].Bounds.Width; + h = (uint)window.Screens.All[0].Bounds.Height; + } + } + else if (OperatingSystem.IsWindows()) + { + Console.Warn("Avalonia backend is not initialized or its windowing API failed to enumerate connected displays."); + w = (uint)Windows.PInvoke.GetSystemMetrics(Windows.PInvoke.SM_CXSCREEN); + h = (uint)Windows.PInvoke.GetSystemMetrics(Windows.PInvoke.SM_CYSCREEN); + } + else if (OperatingSystem.IsLinux()) // Developed on Windows. Tested on WSL Ubuntu. + { + Console.Warn("Avalonia backend is not initialized or its windowing API failed to enumerate connected displays."); + (w, h) = Linux.PInvoke.GetScreenResolutionFromXrandr(); + } + else if (OperatingSystem.IsMacOS()) + { + throw new NotSupportedException("This API feature does not yet support Mac OS"); + } + + if (w is 0 || h is 0) + throw new InvalidOperationException("Unable to query the current resolution of any display/screen."); + + Console.Info($"Monitor resolution successfully acquired. ({w}x{h})"); + return (w, h); + } +} From d67e420076e61a3f293c2effa7385752f2cf43cd Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 16 Jul 2023 18:01:00 -0700 Subject: [PATCH 244/276] style(editorconfig): update xml/xaml/axaml indent/align style --- .editorconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8476940a..987023e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# editorconfig.org +# editorconfig.org # You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference # top-most EditorConfig file @@ -17,8 +17,11 @@ insert_final_newline = true [.editorconfig] insert_final_newline = true -[*.{xml,xaml}] -indent_style = tab +[*.{xml,xaml,axaml}] +indent_style = space +max_line_length = off +xml_alignment_tab_fill_style = use_spaces +xml_pi_attributes_indent = align_by_first_attribute [project.json] indent_size = 2 From eec7ff62ff98ac6f362f48e52425b2e7429b4851 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 16 Jul 2023 18:05:21 -0700 Subject: [PATCH 245/276] chore(vscode): remove framework switcher --- .vscode/extensions.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c6d302b9..0da008d8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -40,7 +40,6 @@ "josefpihrt-vscode.roslynator", "tobias-sekan.solutionextension", "mpontus.tab-cycle", - "yamachu.targetframeworksswitcher", "wayou.vscode-todo-highlight", "pflannery.vscode-versionlens", "rstm-sf.view-avalonia-preview", From fd6ee0dda519a0b6378d03df2c91126fdfc8c6b1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 16 Jul 2023 18:10:08 -0700 Subject: [PATCH 246/276] chore(vscode): add launch config (console, --liveViewAll) --- .vscode/launch.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 70a2c884..0db6abc7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -39,6 +39,18 @@ "justMyCode": false, "requireExactSource": false }, + { + "name": ".NET Core Launch (console, --liveViewAll)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/Latest/HXE.dll", + "args": [ + "--liveViewAll" + ], + "cwd": "${workspaceFolder}/bin/Debug/Latest/", + "enableStepFiltering": false + }, { "name": ".NET Core Launch (console, --positions)", "type": "coreclr", From 933fec51b8525d8928790c022d40c489878419be Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 16 Jul 2023 18:10:51 -0700 Subject: [PATCH 247/276] chore(vscode): add launch config ".NET Core Watch (--liveViewAll)" --- .vscode/launch.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 0db6abc7..57438a80 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,14 @@ { "version": "0.2.0", "configurations": [ + { + "type": "DotNetWatch", + "request": "launch", + "name": ".NET Core Watch (--liveViewAll)", + "args": [ + "--liveViewAll" + ] + }, { // Use IntelliSense to find out which attributes exist for C# debugging // Use hover for the description of the existing attributes From 3ca22ada2063612959fbd9e43e515d819ecd20d9 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 16 Jul 2023 18:11:19 -0700 Subject: [PATCH 248/276] chore(vscode): update launch config URLs --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 57438a80..1f403b8f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,7 @@ { // Use IntelliSense to find out which attributes exist for C# debugging // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md "name": ".NET Core Launch (console)", "type": "coreclr", "request": "launch", @@ -98,4 +98,4 @@ "request": "attach" } ] -} +} \ No newline at end of file From 4cbbd44a302bf6c156b76212df7c68e0502ac298 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 16 Jul 2023 18:41:35 -0700 Subject: [PATCH 249/276] chore: simplify GitVersion config --- GitVersion.yml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index 2f504770..1318887b 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,15 +1,5 @@ -# https://github.com/GitTools/GitVersion/blob/main/docs/input/docs/reference/configuration.md -mode: ContinuousDeployment -tag-prefix: '[vV]' -major-version-bump-message: "^(breaking|build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)" -minor-version-bump-message: "^(feat)(\\([\\w\\s]*\\))?:" -patch-version-bump-message: "^(fix|perf)(\\([\\w\\s]*\\))?:" -branches: - develop: - increment: none # IMPORTANT - main: - increment: none # IMPORTANT - is-release-branch: true -ignore: - sha: [] -merge-message-formats: {} +# https://gitversion.net/docs/reference/configuration +mode: MainLine +major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)" +minor-version-bump-message: "^(feat)(\\([\\w\\s-]*\\))?:" +patch-version-bump-message: "^(build|chore|ci|docs|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?:" From af723e4c3d2eb962d4aee9e94500be9719e2e511 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Thu, 21 Mar 2024 22:18:31 -0700 Subject: [PATCH 250/276] chore(vscode): add ext rec. ms-dotnettools.csdevkit --- .vscode/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0da008d8..31b0fe9a 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -52,5 +52,6 @@ "rogalmic.vscode-xml-complete", "dotjoshjohnson.xml", "joshbolduc.commitlint", + "ms-dotnettools.csdevkit", ] } From 0a728e5d8f7716e715904e81391a09d87968fc63 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Thu, 21 Mar 2024 22:34:27 -0700 Subject: [PATCH 251/276] chore(vscode): add ext rec. avaloniateam.vscode-avalonia --- .vscode/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 31b0fe9a..20e8430c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -53,5 +53,6 @@ "dotjoshjohnson.xml", "joshbolduc.commitlint", "ms-dotnettools.csdevkit", + "avaloniateam.vscode-avalonia", ] } From 704c617de39fcd56b857124f6821da919db8e516 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Fri, 22 Mar 2024 21:41:28 -0700 Subject: [PATCH 252/276] feat: add static property HXE.App.App enables static access to the current instance of HXE.App...if the current Avalonia App is HXE.App. --- src/App.axaml.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index ad46cea2..5a376a7f 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -6,6 +6,9 @@ namespace HXE; public partial class App : Application { + /// Null when not initialized or when Avalonia.Application.Current is not HXE.App + public static new App? Current => Application.Current as App; + public override void Initialize() { AvaloniaXamlLoader.Load(this); From 0827cccd2da7488bbabe4063749fb9b059fcefa1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Fri, 22 Mar 2024 21:43:01 -0700 Subject: [PATCH 253/276] refactor: fix naming violation --- src/MainWindow.axaml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MainWindow.axaml.cs b/src/MainWindow.axaml.cs index b1391787..fa9db1cd 100644 --- a/src/MainWindow.axaml.cs +++ b/src/MainWindow.axaml.cs @@ -21,7 +21,7 @@ public Settings Settings { if (_settings is null) { - queryExistingWindows(); + QueryExistingWindows(); if (_settings is null) _settings = new(); } @@ -35,7 +35,7 @@ public Positions Positions { if (_positions is null) { - queryExistingWindows(); + QueryExistingWindows(); if (_positions is null) _positions = new(); } @@ -54,7 +54,7 @@ public void OpenPositions(object sender, RoutedEventArgs args) Positions.Show(this); } - private void queryExistingWindows() + private void QueryExistingWindows() { if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopStyleApplicationLifetime) { From 7141804b33f362cb4a588d5ad2cdf6d79c9d70de Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Fri, 22 Mar 2024 21:43:41 -0700 Subject: [PATCH 254/276] refactor: rename temp var 'lifetime' --- src/MainWindow.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MainWindow.axaml.cs b/src/MainWindow.axaml.cs index fa9db1cd..f5077c04 100644 --- a/src/MainWindow.axaml.cs +++ b/src/MainWindow.axaml.cs @@ -56,9 +56,9 @@ public void OpenPositions(object sender, RoutedEventArgs args) private void QueryExistingWindows() { - if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopStyleApplicationLifetime) + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) { - foreach (var w in desktopStyleApplicationLifetime.Windows) + foreach (var w in lifetime.Windows) { if (_settings is null && w is Settings settings) _settings = settings; From c6a74c859a2c43dadf827a65855a684faa8a22f8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Fri, 22 Mar 2024 21:45:07 -0700 Subject: [PATCH 255/276] refactor: remove 'partial' modifier --- src/App.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 5a376a7f..7f4b9994 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -4,7 +4,7 @@ namespace HXE; -public partial class App : Application +public class App : Application { /// Null when not initialized or when Avalonia.Application.Current is not HXE.App public static new App? Current => Application.Current as App; From 620122f90f69f6e35baf87447e5074d7a201eced Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Fri, 22 Mar 2024 23:06:09 -0700 Subject: [PATCH 256/276] refactor: allow trying to run MainWindow in SingleView app lifetime --- src/App.axaml.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 7f4b9994..ed490f1a 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,3 +1,4 @@ +using System; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -17,9 +18,10 @@ public override void Initialize() public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow(); - } + desktop.MainWindow ??= new MainWindow(); + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView) + singleView.MainView = new MainWindow(); + else throw new NotSupportedException($"HXE's {nameof(App)} does not support running {nameof(ApplicationLifetime)} of type '{ApplicationLifetime?.GetType().FullName ?? ""}'."); base.OnFrameworkInitializationCompleted(); } From 93992f13ed0d943cd569ce5a69db3ea08c2ce9f5 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:43:25 -0700 Subject: [PATCH 257/276] refactor(deps): update all deps - Avalonia monorepo: 11.0.10 - MessageBox.Avalonia: 3.1.5.1 - GitVersion.MsBuild: 5.12.0 --- src/HXE.csproj | 19 ++++++++----------- src/Positions.axaml.cs | 10 +++++----- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 638d70e7..2634a2f6 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -148,18 +148,15 @@
- - + + - - - - - - - - - all + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Positions.axaml.cs b/src/Positions.axaml.cs index 86302316..37f6d082 100644 --- a/src/Positions.axaml.cs +++ b/src/Positions.axaml.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * Copyright (c) 2023 Noah Sherwin * @@ -26,7 +26,7 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Platform.Storage; -using MessageBox.Avalonia; +using MsBox.Avalonia; using static HXE.Console; namespace HXE @@ -144,13 +144,13 @@ private void BrowseTarget(object sender, RoutedEventArgs e) private void ShowError(string v) // TODO refactor to public or internal class. I'll probably use it everywhere where I need an error message dialog box. { Error(v); - MessageBoxManager.GetMessageBoxStandardWindow(new MessageBox.Avalonia.DTO.MessageBoxStandardParams() + MessageBoxManager.GetMessageBoxStandard(new MsBox.Avalonia.Dto.MessageBoxStandardParams() { - Icon = MessageBox.Avalonia.Enums.Icon.Error, + Icon = MsBox.Avalonia.Enums.Icon.Error, ContentTitle = "Error", WindowStartupLocation = WindowStartupLocation.CenterOwner, ContentMessage = v - }).ShowDialog(this); + }).ShowWindowDialogAsync(this); } } } From d47053c8c8bb5a2523d444748322acc57f557168 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:47:26 -0700 Subject: [PATCH 258/276] chore: fix casing requirement in GitVersion.yml --- GitVersion.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GitVersion.yml b/GitVersion.yml index 1318887b..486d7320 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,5 +1,5 @@ # https://gitversion.net/docs/reference/configuration -mode: MainLine +mode: Mainline major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)" minor-version-bump-message: "^(feat)(\\([\\w\\s-]*\\))?:" patch-version-bump-message: "^(build|chore|ci|docs|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?:" From df5fc6f6be88fd2b8efc1b14df63b830132d3b4f Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:48:03 -0700 Subject: [PATCH 259/276] style: set `csharp_style_var_elsewhere` to `false:none` --- .editorconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 987023e9..03d8a539 100644 --- a/.editorconfig +++ b/.editorconfig @@ -140,8 +140,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter # Types: use keywords instead of BCL types, and permit var only when the type is clear csharp_style_var_for_built_in_types = false:suggestion csharp_style_var_when_type_is_apparent = false:none -csharp_style_var_elsewhere = false:suggestion - +csharp_style_var_elsewhere = false:none # Code style defaults csharp_using_directive_placement = outside_namespace:suggestion From f4578c624172110b46157e96f17035e63cfa5eb6 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:49:09 -0700 Subject: [PATCH 260/276] refactor: move Solution to repo root --- src/HXE.sln => HXE.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/HXE.sln => HXE.sln (89%) diff --git a/src/HXE.sln b/HXE.sln similarity index 89% rename from src/HXE.sln rename to HXE.sln index da6b34b8..9ed020da 100644 --- a/src/HXE.sln +++ b/HXE.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31612.314 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HXE", "HXE.csproj", "{ACAA5D9F-B23D-43E1-B2DF-8C03230975A1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HXE", "src\HXE.csproj", "{ACAA5D9F-B23D-43E1-B2DF-8C03230975A1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 446cc4e42a6369385a4bad07d47c93f8058213f8 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:53:05 -0700 Subject: [PATCH 261/276] chore(vscode): add more terms to cspell dictionary --- .vscode/settings.json | 81 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index db29dfa3..7d70dbe8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,11 +17,88 @@ "pull-requests" ], "omnisharp.useModernNet": true, - "omnisharp.enableRoslynAnalyzers": true, "omnisharp.organizeImportsOnFormat": true, "cSpell.words": [ + "aldrv", + "Anaglyphic", + "arctan", + "Autoaim", + "AUTOCENTER", + "Avalonia", + "bink", + "blam", + "Borderless", + "cinemabars", + "Cinematics", + "CXSCREEN", + "CYSCREEN", + "DBUG", + "deband", + "Descope", + "DESCOPING", + "Deuteranopes", + "devmode", + "dinput", + "distros", + "DLGFRAME", + "Eaxir", + "Ecran", + "EXEP", + "Gameloop", + "Gamepad", + "Gamepads", + "geteuid", "haloce", - "Avalonia" + "HCE's", + "HKEY", + "HTMCC", + "HUDHUD", + "initc", + "isdst", + "lastprof", + "LASTPROFILE", + "libraryfolders", + "libxrandr", + "Lightmaps", + "Lumoria", + "LZNT", + "MAXIMIZEBOX", + "mday", + "mheight", + "MINIMIZEBOX", + "mwidth", + "MXAO", + "nogamma", + "NOMOVE", + "NOSIZE", + "novideo", + "NOZORDER", + "opensauce", + "PIDVID", + "POMB", + "precache", + "Printscreen", + "profdir", + "runas", + "SAFEMODE", + "savegame", + "SHOWWINDOW", + "SIZEBOX", + "steamapps", + "steamexepath", + "Syscall", + "SYSMENU", + "Tritanopes", + "Ungrab", + "vidmode", + "Vsync", + "wday", + "XBCD", + "XRRGetCrtc", + "XRRSetCrtc", + "yday", + "Yelo", + "yumiris" ], "MS-CST-E.vscode-devskim.ignores.ignoreRulesList": [ "DS187371" From 4320bceb161221c80a95a3d0bfe9aac71d897460 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:53:30 -0700 Subject: [PATCH 262/276] chore(vscodE): remove unused settings --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7d70dbe8..4e4774c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,9 +100,6 @@ "Yelo", "yumiris" ], - "MS-CST-E.vscode-devskim.ignores.ignoreRulesList": [ - "DS187371" - ], "dotnet.defaultSolution": "src\\HXE.sln", "xml.fileAssociations": [ { From 995dad954e7ec4808ee2f2289663e416b061b2b5 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 01:54:06 -0700 Subject: [PATCH 263/276] chore(vscode): enforce GitVersion 5.12 cfg schema --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4e4774c6..08632ce8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -113,5 +113,7 @@ "enabled": false } ], - "sarif-viewer.connectToGithubCodeScanning": "off" + "yaml.schemas": { + "https://raw.githubusercontent.com/GitTools/GitVersion/main/schemas/5.12/GitVersion.configuration.json": "file:///c%3A/Repos/HaloSPV3/HXE/GitVersion.yml" + } } From 9b17be11859a9da9237d93186a02a6a6ed0230af Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 02:10:04 -0700 Subject: [PATCH 264/276] chore(deps): bind all Avalonia package versions to AvaloniaVersion --- src/HXE.csproj | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 2634a2f6..927546e0 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -147,14 +147,18 @@ + + 11.0.10 + + - - + + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive From 75ba6978bdafb3c9090a146aff876dbdc30bff56 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 02:11:04 -0700 Subject: [PATCH 265/276] refactor(deps): add Avalonia.ReactiveUI for DefaultExceptionHandler --- src/App.axaml.cs | 11 +++++++++++ src/HXE.csproj | 1 + 2 files changed, 12 insertions(+) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index ed490f1a..df738571 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -2,6 +2,9 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using Avalonia.Reactive; +using ReactiveUI; + namespace HXE; @@ -23,6 +26,14 @@ public override void OnFrameworkInitializationCompleted() singleView.MainView = new MainWindow(); else throw new NotSupportedException($"HXE's {nameof(App)} does not support running {nameof(ApplicationLifetime)} of type '{ApplicationLifetime?.GetType().FullName ?? ""}'."); + // Here we subscribe to ReactiveUI default exception handler to avoid app + // termination in case if we do something wrong in our view models. See: + // https://www.reactiveui.net/docs/handbook/default-exception-handler/ + // + // In case if you are using another MV* framework, please refer to its + // documentation explaining global exception handling. + RxApp.DefaultExceptionHandler = new AnonymousObserver(ex => Console.Error(ex.ToString())); + base.OnFrameworkInitializationCompleted(); } } diff --git a/src/HXE.csproj b/src/HXE.csproj index 927546e0..f4bf851e 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -159,6 +159,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive From 78677ead01b868ffc8842e9200c8fd4564908fa4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 24 Mar 2024 02:13:40 -0700 Subject: [PATCH 266/276] refactor: remove design elements from Window controls --- src/MainWindow.axaml | 3 --- src/Positions.axaml | 5 +---- src/Settings.axaml | 3 --- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/MainWindow.axaml b/src/MainWindow.axaml index 535de9c5..b21be9bd 100644 --- a/src/MainWindow.axaml +++ b/src/MainWindow.axaml @@ -1,8 +1,5 @@ diff --git a/src/Positions.axaml b/src/Positions.axaml index 96cdb596..344770f0 100644 --- a/src/Positions.axaml +++ b/src/Positions.axaml @@ -1,4 +1,4 @@ - - win7-x64;win7-x86;linux-x64;linux-musl-x64 + win7-x64;win7-x86;win-arm64;linux-x64;linux-arm64 HXE.Program HXE HXE From ff5ab5683dd5f00b92f61e3bcf2f8e8c987a641d Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:17:31 -0700 Subject: [PATCH 270/276] build: update year in property "Copyright" --- src/HXE.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 5c61268d..12005078 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -3,7 +3,7 @@ ..\bin\$(Configuration) AnyCPU false - Exe + WinExe net6.0 win7-x64;win7-x86;win-arm64;linux-x64;linux-arm64 @@ -70,7 +70,7 @@ HXE $(ProductName) A HCE wrapper and SPV3 loader - Copyright © 2023 Noah Sherwin + Copyright © 2024 Noah Sherwin Zlib https://github.com/HaloSPV3/HXE https://github.com/HaloSPV3/HXE/blob/main/src/Assets/icon.png From 42478bf58df662b991b3812527c24bf145c00021 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:19:14 -0700 Subject: [PATCH 271/276] build: disable BuiltInComInteropSupport --- src/HXE.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 12005078..6c424cf6 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -27,7 +27,6 @@ en true enable - true app.manifest true true From 2932f35ac91b5f318a0877b9459ceda4f6952caa Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:20:26 -0700 Subject: [PATCH 272/276] build: enable EnableCompressionInSingleFile for Release unconditionally --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 6c424cf6..4b73a5b9 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -57,7 +57,7 @@ true true - true + true From b6c8145dd3a61b3a78ec64035ab225d7b5b3edc1 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:22:42 -0700 Subject: [PATCH 273/276] build: allow embedding the debug symbols in Linux builds --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index 4b73a5b9..2d282e94 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -42,6 +42,7 @@ $(DefineConstants);ENABLE_XAML_HOT_RELOAD + false From 4427deb129dbc4155cfd8fa72fdbd372a94fbbe4 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:23:03 -0700 Subject: [PATCH 274/276] build: allow Unsafe blocks --- src/HXE.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HXE.csproj b/src/HXE.csproj index 2d282e94..3a1c41fd 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -31,6 +31,7 @@ true true true + true From 57aaa784e367ee49588d09407fe93211d638f5b3 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:24:37 -0700 Subject: [PATCH 275/276] build: move prop Description to main prop group --- src/HXE.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 3a1c41fd..61ec1cde 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -20,6 +20,7 @@ true + A HCE wrapper and SPV3 loader {ACAA5D9F-B23D-43E1-B2DF-8C03230975A1} Properties HXE @@ -70,7 +71,7 @@ github/HaloSPV3 HXE $(ProductName) - A HCE wrapper and SPV3 loader + $(Description) Copyright © 2024 Noah Sherwin Zlib https://github.com/HaloSPV3/HXE From 6246e6dc3e0a898a2dd7f9223f408ce145e8af59 Mon Sep 17 00:00:00 2001 From: Noah Sherwin Date: Sun, 7 Jul 2024 00:25:01 -0700 Subject: [PATCH 276/276] build: enable PublishTrimmed for Release --- src/HXE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HXE.csproj b/src/HXE.csproj index 61ec1cde..986a46bd 100644 --- a/src/HXE.csproj +++ b/src/HXE.csproj @@ -48,7 +48,7 @@ - + true TRACE true win7-x86