diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aaafcec2..7606e9e8 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" + TFM: "net6.0-windows" ASSEMBLYNAME: "HXE" PROJPATH: "./src/HXE.csproj" + strategy: + fail-fast: false + matrix: + RID: [ "win-x86", "win-x64" ] steps: ################ @@ -37,15 +40,12 @@ jobs: - run: npm install # Dependents: Semantic Release - # Authenticates packages to push to GPR - uses: actions/setup-dotnet@v1 with: - dotnet-version: "6.0.x" # SDK Version to use. + dotnet-version: "6.0.x" include-prerelease: true - - name: Add GitHub Package Repository run: dotnet nuget add source https://nuget.pkg.github.com/HaloSPV3/index.json -n "github" -u USERNAME -p ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text - - name: Set NuGet.org Credentials run: dotnet nuget update source "nuget.org" -u USERNAME -p ${{ secrets.NUGET_TOKEN }} --store-password-in-clear-text @@ -53,7 +53,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 -t ${{ env.TFM }} --RID ${{ matrix.RID }} -p:ContinuousIntegrationBuild=true # required variables: TargetFramework, RuntimeIdentifier, GitVersion_FullSemVer - name: Compress-PublishArtifacts @@ -61,7 +61,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 @@ -77,7 +77,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: publish-artifacts - path: bin/Release/${{ env.TFM }}/${{ env.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"} ] } ] 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": "" + } + } } diff --git a/README.md b/README.md index 911f3373..6cd2940e 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,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. 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..." } } 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/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/HCE/Executable.cs b/src/HCE/Executable.cs index f897d812..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 * @@ -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)) @@ -238,4 +238,4 @@ public class MiscellaneousOptions public bool NoVideo { get; set; } } } -} \ No newline at end of file +} diff --git a/src/HXE.csproj b/src/HXE.csproj index c09650e2..029ae7bf 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 @@ -34,11 +34,11 @@ https://github.com/HaloSPV3/HXE snupkg - + true - true - win-x86 - + false + win-x86 + true {ACAA5D9F-B23D-43E1-B2DF-8C03230975A1} Properties @@ -47,13 +47,16 @@ true hxe true - true + en-US + true + $([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '6.0.300')) + $(Win7SF) - + x64 false - + x86 true @@ -61,15 +64,21 @@ DEBUG;TRACE + false TRACE + + true + true + true + .root\%(Filename)%(Extension) Never - .root\.github\%(RecursiveDir)%(Filename)%(Extension) + .root\.github\%(RecursiveDir)%(Filename)%(Extension) .docs\%(RecursiveDir)%(Filename)%(Extension) @@ -117,7 +126,10 @@ 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"); diff --git a/src/Kernel.cs b/src/Kernel.cs index d4b18744..d8b4ddc7 100644 --- a/src/Kernel.cs +++ b/src/Kernel.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2019 Emilian Roman * Copyright (c) 2021 Noah Sherwin * @@ -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."); } @@ -1076,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 */ @@ -1107,4 +1109,4 @@ public class ConfigurationTweaks } } } -} \ No newline at end of file +} 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/Positions.xaml.cs b/src/Positions.xaml.cs index 47175737..fdb44202 100644 --- a/src/Positions.xaml.cs +++ b/src/Positions.xaml.cs @@ -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; diff --git a/src/Process.cs b/src/Process.cs index fe911839..2c9334d8 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 @@ -25,8 +25,9 @@ namespace HXE { - public class Process + public static class Process { + private const string ExceptionHeader = " -- Process Inference failed"; public enum Type { Unknown, @@ -38,105 +39,179 @@ 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" } + }; + + public static Result LastResult { get; internal set; } /// - /// Infers the running Halo executable, with support for HCE, HCE and MCC (Steam & Windows Store). + /// 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(); + return LastResult; + } + + /// + /// 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() { - 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) { - var msg = $" -- Process Inference failed{NewLine}Error: { e }{NewLine}"; - var log = (File) Paths.Exception; - log.AppendAllText(msg); - Console.Info(msg); - throw; + 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(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 } + /// Each case sets assigns Success, Type, and Message to LastResult. + /// If a valid process is found... + /// ...LastResult.Success is set to true (else, false) + /// ...LastResult.Type is set to the matching Type (else, unknown) + /// ...LastResult.Message is set to an informative string (else, still informative) + /// ...DeeperCheck returns a bool representing whether the current Candidate is a match switch (process.ProcessName) { case "halo": + LastResult.Success = InspectHPC(); + LastResult.Type = Type.Retail; + return LastResult.Success; case "haloce": - { - try - { - return process.MainModule.FileVersionInfo.FileVersion == "01.00.10.0621"; - } - catch (System.Exception e) - { - ErrorOutput(e, "Failed to assess Halo/HaloCE process."); - return false; - } - } + LastResult.Success = InspectHPC(); + LastResult.Type = Type.HCE; + return LastResult.Success; case string a when a.Contains("MCC") && a.Contains("WinStore"): // redundant, but good practice case "MCC-Win64-Shipping-WinStore": case "MCCWinStore-Win64-Shipping": + LastResult.Success = InspectMCC(); + LastResult.Type = Type.Store; + return LastResult.Success; case "MCC-Win64-Shipping": - { - try - { - return process.Modules - .Cast() - .Any(module => module.ModuleName == Paths.MCC.H1dll); - } - 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."; - ErrorOutput(e, msg2); - return false; - } - } + LastResult.Success = InspectMCC(); + LastResult.Type = Type.Steam; + return LastResult.Success; default: return false; } - void ErrorOutput(System.Exception e, string msg2) + + bool InspectHPC() + { + try + { + bool isValid = process.MainModule.FileVersionInfo.FileVersion == "01.00.10.0621"; + LastResult.Message = isValid ? + "Valid Halo/HaloCE process found" : + "Discovered a Halo/HaloCE process, but its version does not match 01.00.10.0621"; + return isValid; + } + catch (System.Exception e) + { + const string msg = "Failed to assess Halo/HaloCE process"; + LastResult.Message = msg; + ErrorOutput(e, msg); + return false; + } + } + + bool InspectMCC() { - var msg = $" -- Process Inference failed{NewLine}{msg2}{NewLine}Error: { e }{NewLine}"; - var log = (File) Paths.Exception; - log.AppendAllText(msg); - Console.Error(msg); ; + 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; + } } } + 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 { public Type Type { get; set; } public string Name { get; set; } } + + public class Result + { + public bool Success { get; set; } + public Type Type { get; set; } + public string Message { get; set; } + } } -} \ No newline at end of file +} diff --git a/src/Program.cs b/src/Program.cs index 7fa167ae..bb40a6ca 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -22,7 +22,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Reflection; +using System.Linq; using System.Threading.Tasks; using System.Windows; using HXE.HCE; @@ -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
@@ -76,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); @@ -84,6 +86,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 +108,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 */ @@ -131,7 +135,7 @@ private static void InvokeProgram(string[] args) if (help) { options.WriteOptionDescriptions(Out); - Exit(0); + WithCode(Code.Success); } if (test) @@ -151,6 +155,7 @@ private static void InvokeProgram(string[] args) catch (Exception e) { Error("Settings window threw an exception!" + NewLine + e.ToString()); + throw; } try @@ -160,24 +165,36 @@ 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) { Error("Positions window threw an exception!" + NewLine + e.ToString()); + throw; } + WithCode(Code.Success); } if (config) { _ = new Application().Run(new Settings()); - Exit(0); + WithCode(Code.Success); } if (positions) { - _ = new Application().Run(new Positions()); - Exit(0); + if (cli) + { + CLI.Positions.Run(); + } + else + { + _ = new Application().Run(new Positions()); + } + WithCode(Code.Success); } if (infer) @@ -193,9 +210,9 @@ 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); + WithCode(Code.Success); } if (!string.IsNullOrWhiteSpace(install)) @@ -253,9 +270,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); } @@ -369,7 +386,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/Properties/PublishProfiles/Release_net462_fdd.pubxml b/src/Properties/PublishProfiles/Release_net462_fdd.pubxml new file mode 100644 index 00000000..23931aea --- /dev/null +++ b/src/Properties/PublishProfiles/Release_net462_fdd.pubxml @@ -0,0 +1,14 @@ + + + + + Release + Any CPU + false + ..\bin\Release\net462\publish\ + FileSystem + net462 + + diff --git a/src/Properties/PublishProfiles/Release_net6.0-windows_fdd.pubxml b/src/Properties/PublishProfiles/Release_net6.0-windows_fdd.pubxml new file mode 100644 index 00000000..2b1bae69 --- /dev/null +++ b/src/Properties/PublishProfiles/Release_net6.0-windows_fdd.pubxml @@ -0,0 +1,16 @@ + + + + + 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 + + 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') + + diff --git a/src/SFX.cs b/src/SFX.cs index f2ce4e93..82b0b2ba 100644 --- a/src/SFX.cs +++ b/src/SFX.cs @@ -30,7 +30,6 @@ using static System.IO.FileMode; using static System.IO.Path; using static System.IO.SeekOrigin; -using static System.Reflection.Assembly; using static System.Text.Encoding; using static HXE.Console; @@ -366,7 +365,7 @@ public class Configuration public string Filter { get; set; } = "*"; public FileInfo Executable { get; set; } = new FileInfo(System.AppContext.BaseDirectory - ?? throw new InvalidOperationException()); + ?? throw new InvalidOperationException()); } } } 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 @@ -