Update to .NET 7

This commit is contained in:
Fabian
2022-11-10 18:35:27 +01:00
parent f989434d4e
commit 38c6ceeaab
12 changed files with 119 additions and 138 deletions

View File

@@ -11,8 +11,8 @@ Please see our Open Source project [Documentation Repo](https://github.com/Arcti
You can find signed binary releases at [Releases](https://github.com/Arctium/WoW-Launcher/releases)
### Supported Game Versions (Windows x86 64 bit)
* Dragonflight: 10.0.0, 10.0.2
* Shadowlands: 9.1.0, 9.1.5, 9.2.0, 9.2.5, 9.2.7 (default)
* Dragonflight: 10.0.0, 10.0.2 (implicit)
* Shadowlands: 9.1.0, 9.1.5, 9.2.0, 9.2.5, 9.2.7 (implicit)
* Classic: 2.5.2, 2.5.3, 2.5.4, 3.4.0 (--version Classic)
* Classic Era: 1.14.x (--version ClassicEra)
@@ -24,13 +24,13 @@ You can find signed binary releases at [Releases](https://github.com/Arctium/WoW
## Building
### Build Prerequisites
* [.NET Core SDK 6.0.0 or later](https://dotnet.microsoft.com/download/dotnet/6.0)
* [.NET Core SDK 7.0.0 or later](https://dotnet.microsoft.com/download/dotnet/7.0)
* Optional for native builds: C++ workload through Visual Studio 2022 or latest C++ build tools
### Build Instructions Windows (native)
* Available runtime identifiers/platforms: win-x64/x64, win-arm64/ARM64
* Available release configurations: Release, ReleaseSilentMode, ReleaseCustomFiles, ReleaseCustomFilesSilentMode
* Execute `dotnet publish -r RuntimeIdentifier -c Configuration -p:platform="Platform" --self-contained`
* Execute `dotnet publish -r RuntimeIdentifier -c Configuration -p:platform="Platform"`
* Native output is placed in `build\Configuration\bin\native`
## Usage

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="dotnet7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -1,77 +1,76 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputPath>../build/$(Configuration)/bin</OutputPath>
<AssemblyName>Arctium WoW Launcher</AssemblyName>
<ApplicationIcon>logo.ico</ApplicationIcon>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>False</AppendRuntimeIdentifierToOutputPath>
<Nullable>disable</Nullable>
<LangVersion>preview</LangVersion>
<AssemblyVersion>10.0.0.0</AssemblyVersion>
<FileVersion>10.0.0.0</FileVersion>
<Copyright>Arctium</Copyright>
<Platforms>x64;ARM64</Platforms>
<ImplicitUsings>enable</ImplicitUsings>
<DefineConstants>$(DefineConstants);$(Platform)</DefineConstants>
<Configurations>Debug;Release;ReleaseSilentMode;ReleaseCustomFiles;ReleaseCustomFilesSilentMode</Configurations>
<PropertyGroup>
<OutputPath>../build/$(Configuration)/bin</OutputPath>
<AssemblyName>Arctium WoW Launcher</AssemblyName>
<ApplicationIcon>logo.ico</ApplicationIcon>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>False</AppendRuntimeIdentifierToOutputPath>
<Nullable>disable</Nullable>
<LangVersion>preview</LangVersion>
<AssemblyVersion>9.0.0.0</AssemblyVersion>
<FileVersion>9.0.0.0</FileVersion>
<Copyright>Arctium</Copyright>
<Platforms>x64;ARM64</Platforms>
<ImplicitUsings>enable</ImplicitUsings>
<DefineConstants>$(DefineConstants);$(Platform)</DefineConstants>
<Configurations>Debug;Release;ReleaseSilentMode;ReleaseCustomFiles;ReleaseCustomFilesSilentMode</Configurations>
<!-- Native AOT settings -->
<PublishAot>true</PublishAot>
<RootAllApplicationAssemblies>false</RootAllApplicationAssemblies>
<IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<EventSourceSupport>false</EventSourceSupport>
<InvariantGlobalization>true</InvariantGlobalization>
<IlcScanReflection>false</IlcScanReflection>
<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
<IlcTrimMetadata>true</IlcTrimMetadata>
<IlcOptimizationPreference>Speed</IlcOptimizationPreference>
<GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<!-- Native AOT settings -->
<RootAllApplicationAssemblies>false</RootAllApplicationAssemblies>
<IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);$(Platform);CUSTOM_FILES</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);$(Platform);CUSTOM_FILES</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleaseCustomFiles'">
<DefineConstants>$(DefineConstants);$(Platform);CUSTOM_FILES</DefineConstants>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleaseCustomFiles'">
<DefineConstants>$(DefineConstants);$(Platform);CUSTOM_FILES</DefineConstants>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleaseSilentMode'">
<OutputType>WinExe</OutputType>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleaseSilentMode'">
<OutputType>WinExe</OutputType>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleaseCustomFilesSilentMode'">
<OutputType>WinExe</OutputType>
<DefineConstants>$(DefineConstants);$(Platform);CUSTOM_FILES</DefineConstants>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
<PackageReference Include="System.CommandLine" Version="2.0.0-*" />
</ItemGroup>
<ItemGroup>
<TrimmerRootAssembly Include="mscorlib" />
<TrimmerRootAssembly Include="System.Runtime" />
<TrimmerRootAssembly Include="System.Diagnostics.FileVersionInfo" />
<TrimmerRootAssembly Include="System.Threading.Thread" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='ReleaseCustomFilesSilentMode'">
<OutputType>WinExe</OutputType>
<DefineConstants>$(DefineConstants);$(Platform);CUSTOM_FILES</DefineConstants>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="7.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-*" />
</ItemGroup>
</Project>

View File

@@ -142,7 +142,7 @@ class WinMemory
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
Program.CancellationTokenSource.Cancel();
Launcher.CancellationTokenSource.Cancel();
}
while (Read(patchOffset, patch.Length)?.SequenceEqual(patch) == false)
@@ -163,7 +163,7 @@ class WinMemory
public Task QueuePatch(long patchOffset, byte[] patch, string patchName)
{
Program.CancellationTokenSource.Token.ThrowIfCancellationRequested();
Launcher.CancellationTokenSource.Token.ThrowIfCancellationRequested();
Console.WriteLine($"[{patchName}] Adding...");
@@ -198,7 +198,7 @@ class WinMemory
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
Program.CancellationTokenSource.Cancel();
Launcher.CancellationTokenSource.Cancel();
return Task.CompletedTask;
}
@@ -234,12 +234,12 @@ class WinMemory
// Apply our patches.
foreach (var p in patchList)
{
var address = p.Value.Item1;
var address = p.Value.Address;
if (address == 0)
continue;
var patch = p.Value.Item2;
var patch = p.Value.Data;
// We are in a different section here.
if (address > Data.Length)
@@ -311,7 +311,7 @@ class WinMemory
{
try
{
if (NtQueryInformationProcess(processHandle, 0, ref peb, peb.Size, out int sizeInfoReturned) == NtStatus.Success)
if (NtQueryInformationProcess(processHandle, 0, ref peb, ProcessBasicInformation.Size, out int sizeInfoReturned) == NtStatus.Success)
return Read(peb.PebBaseAddress + 0x10);
}
catch (Exception ex)
@@ -323,19 +323,19 @@ class WinMemory
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsShortJump(byte[] instructions, int startIndex = 0)
public static bool IsShortJump(byte[] instructions, int startIndex = 0)
{
return instructions[startIndex] >= 0x70 && instructions[startIndex] < 0x7F;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsJump(byte[] instructions, int startIndex = 0)
public static bool IsJump(byte[] instructions, int startIndex = 0)
{
return instructions[startIndex] == 0x0F && instructions[startIndex + 1] >= 0x80 && instructions[startIndex + 1] <= 0x8F;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsUnconditionalJump(byte[] instructions, int startIndex = 0)
public static bool IsUnconditionalJump(byte[] instructions, int startIndex = 0)
{
return instructions[startIndex] == 0xE9 || instructions[startIndex] == 0xEB;
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) Arctium.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;
@@ -22,7 +21,7 @@ static class LaunchOptions
.UseSuggestDirective()
.Build();
public static RootCommand RootCommand = new("Arctium\0WoW\0Launcher")
public static RootCommand RootCommand = new("Arctium WoW Launcher")
{
Version,
GamePath,
@@ -38,4 +37,3 @@ static class LaunchOptions
return rootCommand;
}
}

View File

@@ -9,6 +9,8 @@ namespace Arctium.WoW.Launcher;
class Launcher
{
public static CancellationTokenSource CancellationTokenSource => new();
public static string PrepareGameLaunch(ParseResult commandLineResult)
{
var gameVersion = commandLineResult.GetValueForOption(LaunchOptions.Version);
@@ -24,7 +26,6 @@ class Launcher
GameVersion.ClassicEra => ("_classic_era_", "WowClassic-arm64.exe", new[] { 1 }, 40347),
#endif
_ => throw new NotImplementedException("Invalid game version specified."),
};
Console.ForegroundColor = ConsoleColor.Yellow;
@@ -58,7 +59,7 @@ class Launcher
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"[Error] No {gameVersion} client found.");
return String.Empty;
return string.Empty;
}
var gameClientBuild = GetVersionValueFromClient(gameBinaryPath).Build;
@@ -69,7 +70,7 @@ class Launcher
Console.WriteLine($"Your found client version {gameClientBuild} is not supported.");
Console.WriteLine($"The minimum required build is {MinGameBuild}");
return String.Empty;
return string.Empty;
}
// Delete the cache folder by default.
@@ -99,11 +100,11 @@ class Launcher
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Starting WoW client...");
var createSuccess = NativeWindows.CreateProcess(null, $"{appPath} {gameCommandLine}", 0, 0, false, 4U, 0, new FileInfo(appPath)?.DirectoryName, ref startupInfo, out processInfo);
var createSuccess = NativeWindows.CreateProcess(null, $"{appPath} {gameCommandLine}", 0, 0, false, 4, 0, new FileInfo(appPath)?.DirectoryName, ref startupInfo, out processInfo);
// On some systems we have to launch the game with the application name used.
if (!createSuccess)
createSuccess = NativeWindows.CreateProcess(appPath, $" {gameCommandLine}", 0, 0, false, 4U, 0, null, ref startupInfo, out processInfo);
createSuccess = NativeWindows.CreateProcess(appPath, $" {gameCommandLine}", 0, 0, false, 4, 0, null, ref startupInfo, out processInfo);
// Start process with suspend flags.
if (createSuccess)
@@ -149,7 +150,7 @@ class Launcher
memory.PatchMemory(Patterns.Common.Portal, Patches.Common.Portal, "Login Portal"),
memory.PatchMemory(Patterns.Common.VersionUrl, versionPatch, "Version URL"),
memory.PatchMemory(Patterns.Windows.LauncherLogin, Patches.Windows.LauncherLogin, "Launcher Login Registry")
}, Program.CancellationTokenSource.Token);
}, CancellationTokenSource.Token);
NativeWindows.NtResumeProcess(processInfo.ProcessHandle);
@@ -178,8 +179,8 @@ class Launcher
{
memory.QueuePatch(Patterns.Windows.CertBundle, Patches.Windows.CertBundle, "CertBundle"),
memory.QueuePatch(Patterns.Windows.CertCommonName, Patches.Windows.CertCommonName, "CertCommonName", 5)
}, Program.CancellationTokenSource.Token);
#if CUSTOM_FILES
}, CancellationTokenSource.Token);
#if CUSTOM_FILES
Task.WaitAll(new[]
{
memory.QueuePatch(Patterns.Windows.LoadByFileId, Patches.Windows.NoJump, "LoadByFileId", 6),
@@ -188,7 +189,7 @@ class Launcher
(clientVersion is (10, _, _, _))
? memory.QueuePatch(Patterns.Windows.LoadByFilePathAlternate, Patches.Windows.NoJump, "LoadByFilePath", 3)
: memory.QueuePatch(Patterns.Windows.LoadByFilePath, Patches.Windows.NoJump, "LoadByFilePath", 3)
}, Program.CancellationTokenSource.Token);
}, CancellationTokenSource.Token);
var (idAlloc, stringAlloc) = ModLoader.LoadFileMappings(processInfo.ProcessHandle);
@@ -197,7 +198,7 @@ class Launcher
if (!ModLoader.HookClient(memory, processInfo.ProcessHandle, idAlloc, stringAlloc))
return false;
}
#endif
#endif
#elif ARM64
Task.WaitAll(new[]
@@ -281,14 +282,14 @@ class Launcher
Buffer.BlockCopy(memory.Data, instructionStart, instructions, 0, 6);
// Skip unconditional jumps.
if (memory.IsUnconditionalJump(instructions))
if (WinMemory.IsUnconditionalJump(instructions))
continue;
var operandValue = 0;
if (memory.IsShortJump(instructions))
if (WinMemory.IsShortJump(instructions))
operandValue = instructions[1] + 2;
else if (memory.IsJump(instructions))
else if (WinMemory.IsJump(instructions))
operandValue = BitConverter.ToInt32(instructions, 2) + 6;
else
throw new InvalidDataException("Invalid operand value.");
@@ -299,7 +300,7 @@ class Launcher
// Find all references of real code parts inside the remap check functions.
Parallel.For(lastAddress, memory.Data.Length, i =>
{
if (memory.IsJump(memory.Data, i))
if (WinMemory.IsJump(memory.Data, i))
{
var jumpOperand = BitConverter.ToInt32(memory.Data, i + 2);
var jumpSize = (int)jumpToValue - i - 6;
@@ -313,7 +314,7 @@ class Launcher
tempPatches.TryAdd($"Jump{i}", (i, jumpBytes));
}
}
else if (memory.IsShortJump(memory.Data, i))
else if (WinMemory.IsShortJump(memory.Data, i))
{
var jumpOperand = memory.Data[i + 1];
var jumpSize = (int)jumpToValue - i - 2;

View File

@@ -1,42 +1,34 @@
// Copyright (c) Arctium.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.CommandLine.Invocation;
using System.CommandLine.Parsing;
using Arctium.WoW.Launcher;
using static Arctium.WoW.Launcher.Misc.Helpers;
namespace Arctium.WoW.Launcher;
// "Arctium" should not be removed from the final binary name.
if (!Process.GetCurrentProcess().ProcessName.ToLowerInvariant().Contains("arctium"))
WaitAndExit();
class Program
PrintHeader("WoW Client Launcher");
LaunchOptions.RootCommand.SetHandler(context =>
{
public static CancellationTokenSource CancellationTokenSource { get; private set; }
var appPath = Launcher.PrepareGameLaunch(context.ParseResult);
var gameCommandLine = string.Join(" ", context.ParseResult.UnmatchedTokens);
static async Task Main(string[] args)
{
CancellationTokenSource = new CancellationTokenSource();
if (string.IsNullOrEmpty(appPath) || !Launcher.LaunchGame(appPath, gameCommandLine))
WaitAndExit(5000);
});
PrintHeader("WoW Client Launcher");
await LaunchOptions.Instance.InvokeAsync(args);
LaunchOptions.RootCommand.SetHandler(context =>
{
var appPath = Launcher.PrepareGameLaunch(context.ParseResult);
var gameCommandLine = string.Join(" ", context.ParseResult.UnmatchedTokens);
if (string.IsNullOrEmpty(appPath) || !Launcher.LaunchGame(appPath, gameCommandLine))
WaitAndExit(5000);
});
static void WaitAndExit(int ms = 2000)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine($"Closing in {ms / 1000} seconds...");
await LaunchOptions.Instance.InvokeAsync(args);
}
Thread.Sleep(ms);
public static void WaitAndExit(int ms = 2000)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine($"Closing in {ms / 1000} seconds...");
Thread.Sleep(ms);
Environment.Exit(0);
}
Environment.Exit(0);
}

View File

@@ -13,5 +13,5 @@ struct LargeInteger
[FieldOffset(4)]
public int High;
public int Size => Marshal.SizeOf(typeof(LargeInteger));
public static int Size => Marshal.SizeOf<LargeInteger>();
}

View File

@@ -14,5 +14,5 @@ struct MemoryBasicInformation
public MemProtection Protect;
public MemType Type;
public static int Size => Marshal.SizeOf(typeof(MemoryBasicInformation));
public static int Size => Marshal.SizeOf<MemoryBasicInformation>();
}

View File

@@ -13,5 +13,5 @@ struct ProcessBasicInformation
public nint UniqueProcessId;
public nint InheritedFromUniqueProcessId;
public int Size => Marshal.SizeOf(typeof(ProcessBasicInformation));
public static int Size => Marshal.SizeOf<ProcessBasicInformation>();
}

View File

@@ -10,5 +10,5 @@ struct ProcessInformation
public uint ProcessId;
public uint ThreadId;
public int Size => Marshal.SizeOf(typeof(ProcessInformation));
public static int Size => Marshal.SizeOf<ProcessInformation>();
}

View File

@@ -24,5 +24,5 @@ struct StartupInfo
public nint StdOutputHandle;
public nint StdErrorHandle;
public int Size => Marshal.SizeOf(typeof(StartupInfo));
public static int Size => Marshal.SizeOf<StartupInfo>();
}