diff --git a/README.md b/README.md
index 948f0f7..b49f0a4 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/nuget.config b/nuget.config
deleted file mode 100644
index fe3b040..0000000
--- a/nuget.config
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/Arctium.WoW.Launcher.csproj b/src/Arctium.WoW.Launcher.csproj
index 36ba636..440a417 100644
--- a/src/Arctium.WoW.Launcher.csproj
+++ b/src/Arctium.WoW.Launcher.csproj
@@ -1,77 +1,76 @@
+
+ ../build/$(Configuration)/bin
+ Arctium WoW Launcher
+ logo.ico
+ Exe
+ net7.0
+ True
+ False
+ False
+ disable
+ preview
+ 10.0.0.0
+ 10.0.0.0
+ Arctium
+ x64;ARM64
+ enable
+ $(DefineConstants);$(Platform)
+ Debug;Release;ReleaseSilentMode;ReleaseCustomFiles;ReleaseCustomFilesSilentMode
-
- ../build/$(Configuration)/bin
- Arctium WoW Launcher
- logo.ico
- Exe
- net6.0
- False
- False
- False
- disable
- preview
- 9.0.0.0
- 9.0.0.0
- Arctium
- x64;ARM64
- enable
- $(DefineConstants);$(Platform)
- Debug;Release;ReleaseSilentMode;ReleaseCustomFiles;ReleaseCustomFilesSilentMode
+
+ true
+ false
+ false
+ false
+ true
+ false
+ true
+ false
+ true
+ true
+ Speed
+ false
+
-
- false
- false
- false
-
+
+ win-x64
+
-
- win-x64
-
+
+ win-arm64
+
-
- win-arm64
-
+
+ $(DefineConstants);$(Platform);CUSTOM_FILES
+
-
- $(DefineConstants);$(Platform);CUSTOM_FILES
-
+
+ none
+ false
+
-
- none
- false
-
+
+ $(DefineConstants);$(Platform);CUSTOM_FILES
+ none
+ false
+
-
- $(DefineConstants);$(Platform);CUSTOM_FILES
- none
- false
-
+
+ WinExe
+ none
+ false
+
-
- WinExe
- none
- false
-
-
-
- WinExe
- $(DefineConstants);$(Platform);CUSTOM_FILES
- none
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ WinExe
+ $(DefineConstants);$(Platform);CUSTOM_FILES
+ none
+ false
+
+
+
+
+
diff --git a/src/IO/WinMemory.cs b/src/IO/WinMemory.cs
index 4adfe02..3fa4474 100644
--- a/src/IO/WinMemory.cs
+++ b/src/IO/WinMemory.cs
@@ -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;
}
diff --git a/src/LaunchOptions.cs b/src/LaunchOptions.cs
index 38923be..b706a43 100644
--- a/src/LaunchOptions.cs
+++ b/src/LaunchOptions.cs
@@ -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;
}
}
-
diff --git a/src/Launcher.cs b/src/Launcher.cs
index 1d679cc..c1693fe 100644
--- a/src/Launcher.cs
+++ b/src/Launcher.cs
@@ -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;
diff --git a/src/Program.cs b/src/Program.cs
index 6033ec0..da3a958 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -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);
}
diff --git a/src/Structures/LargeInteger.cs b/src/Structures/LargeInteger.cs
index 359058c..a0eb5f5 100644
--- a/src/Structures/LargeInteger.cs
+++ b/src/Structures/LargeInteger.cs
@@ -13,5 +13,5 @@ struct LargeInteger
[FieldOffset(4)]
public int High;
- public int Size => Marshal.SizeOf(typeof(LargeInteger));
+ public static int Size => Marshal.SizeOf();
}
diff --git a/src/Structures/MemoryBasicInformation.cs b/src/Structures/MemoryBasicInformation.cs
index a036848..83c8928 100644
--- a/src/Structures/MemoryBasicInformation.cs
+++ b/src/Structures/MemoryBasicInformation.cs
@@ -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();
}
diff --git a/src/Structures/ProcessBasicInformation.cs b/src/Structures/ProcessBasicInformation.cs
index dff743e..84a1e07 100644
--- a/src/Structures/ProcessBasicInformation.cs
+++ b/src/Structures/ProcessBasicInformation.cs
@@ -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();
}
diff --git a/src/Structures/ProcessInformation.cs b/src/Structures/ProcessInformation.cs
index 8c1e584..8fac950 100644
--- a/src/Structures/ProcessInformation.cs
+++ b/src/Structures/ProcessInformation.cs
@@ -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();
}
diff --git a/src/Structures/StartupInfo.cs b/src/Structures/StartupInfo.cs
index 96f69ff..1a74e7c 100644
--- a/src/Structures/StartupInfo.cs
+++ b/src/Structures/StartupInfo.cs
@@ -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();
}