Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BedrockLauncher.Core/BedrockCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
MinecraftGameTypeVersion.Beta => _DEFINE_REF2.pre,
_=>null
});
var msiXvdDecoder = new MsiXVDDecoder(cik);
var msiXvdDecoder = new MsiXVDDecoder(cik, options.UseHardwareDecode);
var msiXvdStream = new MsiXVDStream(options.FileFullPath);
msiXvdStream.Parse();
options.InstallStates?.Report(InstallStates.Extracting);
Expand Down Expand Up @@ -244,7 +244,7 @@

if (processes.Length > 0)
{
return processes

Check warning on line 247 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Possible null reference return.

Check warning on line 247 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Possible null reference return.
.Where(p => p.StartTime > startTime)
.OrderBy(p => (p.StartTime - startTime).TotalMilliseconds)
.FirstOrDefault();
Expand All @@ -254,7 +254,7 @@
await Task.Delay(200);
}

return null;

Check warning on line 257 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Possible null reference return.

Check warning on line 257 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Possible null reference return.
}
private static DateTime GetStartTimeSafe(Process proc)
{
Expand Down Expand Up @@ -296,7 +296,7 @@
CreateNoWindow = true
});

Process minecraftProcess = null;

Check warning on line 299 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Converting null literal or possible null value to non-nullable type.

Check warning on line 299 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Converting null literal or possible null value to non-nullable type.


for (int i = 0; i < 10; i++)
Expand Down Expand Up @@ -469,7 +469,7 @@
process = processes.OrderBy(p => p.StartTime).Last();
}

return process;

Check warning on line 472 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Possible null reference return.

Check warning on line 472 in BedrockLauncher.Core/BedrockCore.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Possible null reference return.
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions BedrockLauncher.Core/BedrockLauncher.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<Version>2.0.4.4-dev</Version>
<Version>2.0.4.5-dev</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<Version>2.0.4.4</Version>
<Version>2.0.4.5</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
7 changes: 7 additions & 0 deletions BedrockLauncher.Core/CoreOption/LocalGamePackageOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ public class LocalGamePackageOptions
/// Title Name only being used When it is in Uwp mode
/// </summary>
public string? GameName;

/// <summary>
/// Gets or sets whether to use hardware-accelerated AES-NI/SSE2 decoding for GDK packages.
/// Set to false to use software-based AES decryption (compatible with CPUs lacking AES-NI support).
/// Default is true (hardware decode).
/// </summary>
public bool UseHardwareDecode { get; set; } = true;
}

/// <summary>
Expand Down
159 changes: 151 additions & 8 deletions BedrockLauncher.Core/GdkDecode/MsiXVDDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,59 @@
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using CryptAes = System.Security.Cryptography.Aes;
using SysCrypt = System.Security.Cryptography;

namespace BedrockLauncher.Core.GdkDecode;

public class MsiXVDDecoder
public class MsiXVDDecoder : IDisposable
{
public KeySinagl d;
public KeySinagl t;
private readonly bool _useHardware;
private readonly CryptAes? _swAesD;
private readonly CryptAes? _swAesT;

public MsiXVDDecoder(CikKey key)
public MsiXVDDecoder(CikKey key) : this(key, true)
{
d.Init(key.DKey,true);
t.Init(key.TKey,false);
}

public MsiXVDDecoder(CikKey key, bool useHardware)
{
_useHardware = useHardware;
if (useHardware)
{
d.Init(key.DKey, true);
t.Init(key.TKey, false);
_swAesD = null;
_swAesT = null;
}
else
{
_swAesD = CryptAes.Create();
_swAesD.KeySize = 128;
_swAesD.Key = key.DKey;
_swAesD.Mode = SysCrypt.CipherMode.ECB;
_swAesD.Padding = SysCrypt.PaddingMode.None;

_swAesT = CryptAes.Create();
_swAesT.KeySize = 128;
_swAesT.Key = key.TKey;
_swAesT.Mode = SysCrypt.CipherMode.ECB;
_swAesT.Padding = SysCrypt.PaddingMode.None;
}
}

public int Decrypt(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> tweakIv)
{
if (_useHardware)
return DecryptHardware(input, output, tweakIv);
else
return DecryptSoftware(input, output, tweakIv);
}

#region Hardware Decode (AES-NI / SSE2)

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<byte> Gf128Mul(Vector128<byte> iv, Vector128<byte> mask)
{
Expand All @@ -26,7 +65,7 @@
return Sse2.Xor(tmp1, tmp2);
}

public int Decrypt(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> tweakIv)
private int DecryptHardware(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> tweakIv)
{
if (tweakIv.Length < 16)
return 0;
Expand Down Expand Up @@ -134,10 +173,10 @@

return length;
}


[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void DecryptBlocks8(
public void DecryptBlocks8(
Vector128<byte> in0,
Vector128<byte> in1,
Vector128<byte> in2,
Expand Down Expand Up @@ -268,4 +307,108 @@
out7 = Aes.DecryptLast(b7, key);
}

#endregion

#region Software Decode (managed AES)

private int DecryptSoftware(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> tweakIv)
{
if (tweakIv.Length < 16)
return 0;

int length = Math.Min(input.Length, output.Length);
if (length == 0)
return 0;

int remainingBlocks = length >> 4;
int leftover = length & 0xF;

if (leftover != 0)
remainingBlocks--;

if (remainingBlocks <= 0 && leftover == 0)
return 0;

byte[] tweak = new byte[16];
_swAesT!.EncryptEcb(tweakIv[..16], tweak, SysCrypt.PaddingMode.None);

Span<byte> tempIn = stackalloc byte[16];
Span<byte> tempOut = stackalloc byte[16];
int offset = 0;

while (remainingBlocks > 0)
{
XorBytes(input.Slice(offset, 16), tweak, tempIn);
_swAesD.DecryptEcb(tempIn, tempOut, SysCrypt.PaddingMode.None);

Check warning on line 342 in BedrockLauncher.Core/GdkDecode/MsiXVDDecoder.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Dereference of a possibly null reference.
XorBytes(tempOut, tweak, output.Slice(offset, 16));
Gf128MulSoftware(tweak);
offset += 16;
remainingBlocks--;
}

if (leftover != 0)
{
byte[] finalTweak = tweak[..]; // copy: finalTweak = Gf128Mul(tweak)
Gf128MulSoftware(finalTweak);

XorBytes(input.Slice(offset, 16), finalTweak, tempIn);
_swAesD.DecryptEcb(tempIn, tempOut, SysCrypt.PaddingMode.None);
XorBytes(tempOut, finalTweak, output.Slice(offset, 16));

Span<byte> currentOut = output.Slice(offset, 16);
ReadOnlySpan<byte> nextIn = input.Slice(offset + 16, leftover);
Span<byte> nextOut = output.Slice(offset + 16, leftover);

Span<byte> temp = stackalloc byte[16];

for (int i = 0; i < leftover; i++)
{
nextOut[i] = currentOut[i];
temp[i] = nextIn[i];
}

for (int i = leftover; i < 16; i++)
{
temp[i] = currentOut[i];
}

XorBytes(temp, tweak, tempIn);
_swAesD.DecryptEcb(tempIn, tempOut, SysCrypt.PaddingMode.None);
XorBytes(tempOut, tweak, output.Slice(offset, 16));
}

return length;
}

private static void XorBytes(ReadOnlySpan<byte> a, ReadOnlySpan<byte> b, Span<byte> result)
{
for (int i = 0; i < 16; i++)
result[i] = (byte)(a[i] ^ b[i]);
}

private static void Gf128MulSoftware(Span<byte> iv)
{
int carryBit127 = (iv[15] >> 7) & 1;
int carryBit63 = (iv[7] >> 7) & 1;

for (int i = 7; i > 0; i--)
iv[i] = (byte)((iv[i] << 1) | (iv[i - 1] >> 7));
iv[0] <<= 1;

for (int i = 15; i > 8; i--)
iv[i] = (byte)((iv[i] << 1) | (iv[i - 1] >> 7));
iv[8] <<= 1;

if (carryBit63 != 0) iv[8] ^= 0x01;
if (carryBit127 != 0) iv[0] ^= 0x87;
}

#endregion

public void Dispose()
{
_swAesD?.Dispose();
_swAesT?.Dispose();
GC.SuppressFinalize(this);
}
}
27 changes: 18 additions & 9 deletions TestProject/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using BedrockLauncher.Core;
using BedrockLauncher.Core.CoreOption;
using System.Diagnostics;
using Windows.Management.Deployment;
using BedrockLauncher.Core.Utils;

namespace TestProject
{
Expand All @@ -10,15 +11,23 @@ static void Main(string[] args)
{
var bedrockCore = new BedrockCore();
bedrockCore.InitAsync().Wait();
var launchOptions = new LaunchOptions()

bedrockCore.InstallPackageAsync(new LocalGamePackageOptions()
{
GameFolder = "D:\\BedrockBoot\\bedrock_versions\\1.21.11401",
GameType = MinecraftGameTypeVersion.Release,
MinecraftBuildType = MinecraftBuildTypeVersion.UWP,
LaunchArgs = "minecraft://creator/?Editor=true"
};
var process = bedrockCore.LaunchGameAsync(launchOptions).Result;
Console.WriteLine(process.Id);
FileFullPath = @"D:\BedrockBoot\version_save\1.26.2.insPack",
Type = MinecraftBuildTypeVersion.GDK,
GameTypeVersion = MinecraftGameTypeVersion.Release,
InstallDstFolder = Path.GetFullPath(@"E:\GDK_TestInstall\1.26_2"),
UseHardwareDecode = false,
DeployProgress = new Progress<DeploymentProgress>(p =>
Console.WriteLine($"Deploy: {p.percentage}%")),
ExtractionProgress = new Progress<DecompressProgress>(p =>
Console.WriteLine($"Extract: [{p.CurrentCount}/{p.TotalCount}] {p.FileName}")),
InstallStates = new Progress<InstallStates>(s =>
Console.WriteLine($"State: {s}")),
}).Wait();

Console.WriteLine("Install done.");
}
}
}
Loading