baseline: OpenMU upstream b5a0961 (fresh source)

This commit is contained in:
Acentech Dev
2026-07-14 19:00:35 +03:00
parent 450402e47d
commit 36fc125d5c
11968 changed files with 748705 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
// <copyright file="DefaultKeys.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Network.Xor;
/// <summary>
/// The default xor keys for encryption and decryption.
/// </summary>
public static class DefaultKeys
{
/// <summary>
/// Gets the default 3 byte long XOR key.
/// </summary>
/// <value>
/// The default 3 byte long XOR key.
/// </value>
public static byte[] Xor3Keys { get; } = { 0xFC, 0xCF, 0xAB };
/// <summary>
/// Gets the default 32 byte long XOR key.
/// </summary>
/// <value>
/// The default 32 byte long XOR key.
/// </value>
public static byte[] Xor32Key { get; } =
{
0xAB, 0x11, 0xCD, 0xFE, 0x18, 0x23, 0xC5, 0xA3,
0xCA, 0x33, 0xC1, 0xCC, 0x66, 0x67, 0x21, 0xF3,
0x32, 0x12, 0x15, 0x35, 0x29, 0xFF, 0xFE, 0x1D,
0x44, 0xEF, 0xCD, 0x41, 0x26, 0x3C, 0x4E, 0x4D,
};
}

View File

@@ -0,0 +1,85 @@
// <copyright file="PipelinedXor32Decryptor.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Network.Xor;
using System.Buffers;
using System.IO.Pipelines;
/// <summary>
/// Pipelined decryptor which uses a 32 byte key for a xor encryption.
/// It's typically used to decrypt packets sent by the client to the server.
/// </summary>
public class PipelinedXor32Decryptor : PacketPipeReaderBase, IPipelinedDecryptor
{
private readonly Pipe _pipe = new();
private readonly byte[] _xor32Key;
/// <summary>
/// Initializes a new instance of the <see cref="PipelinedXor32Decryptor"/> class.
/// </summary>
/// <param name="source">The source.</param>
public PipelinedXor32Decryptor(PipeReader source)
: this(source, DefaultKeys.Xor32Key)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PipelinedXor32Decryptor"/> class.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="xor32Key">The xor32 key.</param>
public PipelinedXor32Decryptor(PipeReader source, byte[] xor32Key)
{
if (xor32Key.Length != 32)
{
throw new ArgumentException($"Xor32key must have a size of 32 bytes, but is {xor32Key.Length} bytes long.");
}
this.Source = source;
this._xor32Key = xor32Key;
_ = this.ReadSourceAsync().ConfigureAwait(false);
}
/// <inheritdoc/>
public PipeReader Reader => this._pipe.Reader;
/// <inheritdoc />
protected override ValueTask OnCompleteAsync(Exception? exception)
{
return this._pipe.Writer.CompleteAsync(exception);
}
/// <summary>
/// Reads the mu online packet.
/// Decrypts the packet and writes it into our pipe.
/// </summary>
/// <param name="packet">The mu online packet.</param>
/// <returns><see langword="true" />, if the flush was successful or not required.<see langword="false" />, if the pipe reader is completed and no longer reading data.</returns>
protected override async ValueTask<bool> ReadPacketAsync(ReadOnlySequence<byte> packet)
{
this.DecryptAndWrite(packet);
return await this.TryFlushWriterAsync(this._pipe.Writer).ConfigureAwait(false);
}
private void DecryptAndWrite(ReadOnlySequence<byte> packet)
{
// The next line is getting a span from the writer which is at least as big as the packet.
// As I found out, it's initially about 2 kb in size and gets smaller within further
// usage. If the previous span was used up, a new piece of memory is getting provided for us.
var span = this._pipe.Writer.GetSpan((int)packet.Length);
// we just want to work on a span with the exact size of the packet.
var target = span.Slice(0, (int)packet.Length);
packet.CopyTo(target);
var headerSize = target.GetPacketHeaderSize();
for (var i = target.Length - 1; i > headerSize; i--)
{
target[i] = (byte)(target[i] ^ target[i - 1] ^ this._xor32Key[i % 32]);
}
this._pipe.Writer.Advance(target.Length);
}
}

View File

@@ -0,0 +1,82 @@
// <copyright file="PipelinedXor32Encryptor.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Network.Xor;
using System.Buffers;
using System.IO.Pipelines;
/// <summary>
/// Pipelined encryptor which uses a 32 byte key for a xor encryption.
/// It's typically used to encrypt packet sent by the client to the server.
/// </summary>
public class PipelinedXor32Encryptor : PacketPipeReaderBase, IPipelinedEncryptor
{
private readonly PipeWriter _target;
private readonly Pipe _pipe = new();
private readonly byte[] _xor32Key;
/// <summary>
/// Initializes a new instance of the <see cref="PipelinedXor32Encryptor"/> class.
/// </summary>
/// <param name="target">The target.</param>
public PipelinedXor32Encryptor(PipeWriter target)
: this(target, DefaultKeys.Xor32Key)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PipelinedXor32Encryptor"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// /// <param name="xor32Key">The xor32 key.</param>
public PipelinedXor32Encryptor(PipeWriter target, byte[] xor32Key)
{
if (xor32Key.Length != 32)
{
throw new ArgumentException($"Xor32key must have a size of 32 bytes, but is {xor32Key.Length} bytes long.");
}
this.Source = this._pipe.Reader;
this._target = target;
this._xor32Key = xor32Key;
_ = this.ReadSourceAsync().ConfigureAwait(false);
}
/// <inheritdoc/>
public PipeWriter Writer => this._pipe.Writer;
/// <inheritdoc />
protected override ValueTask OnCompleteAsync(Exception? exception)
{
return this._target.CompleteAsync(exception);
}
/// <summary>
/// Reads the mu online packet.
/// Encrypts the packet and writes it into the target.
/// </summary>
/// <param name="packet">The mu online packet.</param>
/// <returns><see langword="true" />, if the flush was successful or not required.<see langword="false" />, if the pipe reader is completed and no longer reading data.</returns>
protected override async ValueTask<bool> ReadPacketAsync(ReadOnlySequence<byte> packet)
{
this.EncryptAndWrite(packet);
return await this.TryFlushWriterAsync(this._target).ConfigureAwait(false);
}
private void EncryptAndWrite(ReadOnlySequence<byte> packet)
{
var span = this._target.GetSpan((int)packet.Length);
var result = span.Slice(0, (int)packet.Length);
packet.CopyTo(result);
var headerSize = result.GetPacketHeaderSize();
for (int i = headerSize + 1; i < packet.Length; i++)
{
result[i] = (byte)(result[i] ^ result[i - 1] ^ this._xor32Key[i % 32]);
}
this._target.Advance(result.Length);
}
}

View File

@@ -0,0 +1,26 @@
// <copyright file="Xor3Decryptor.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Network.Xor;
/// <summary>
/// A decryptor which XOR-decrypts data using a 3-byte key.
/// </summary>
public class Xor3Decryptor : Xor3Encryptor, ISpanDecryptor
{
/// <summary>
/// Initializes a new instance of the <see cref="Xor3Decryptor"/> class.
/// </summary>
/// <param name="startOffset">The start offset.</param>
public Xor3Decryptor(int startOffset)
: base(startOffset)
{
}
/// <inheritdoc/>
public void Decrypt(Span<byte> packet)
{
this.Encrypt(packet);
}
}

View File

@@ -0,0 +1,43 @@
// <copyright file="Xor3Encryptor.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Network.Xor;
/// <summary>
/// An encryptor which XOR-encrypts data using a 3-byte key.
/// </summary>
public class Xor3Encryptor : ISpanEncryptor
{
private readonly byte[] _xor3Keys;
private readonly int _startOffset;
/// <summary>
/// Initializes a new instance of the <see cref="Xor3Encryptor"/> class.
/// </summary>
/// <param name="startOffset">The start offset.</param>
public Xor3Encryptor(int startOffset)
{
this._startOffset = startOffset;
this._xor3Keys = DefaultKeys.Xor3Keys;
}
/// <inheritdoc/>
public void Encrypt(Span<byte> data)
{
this.InternalEncrypt(data.Slice(this._startOffset));
}
/// <summary>
/// Internal encrypt function. XORs each byte with one byte of the 3-byte key.
/// </summary>
/// <param name="data">The data.</param>
protected void InternalEncrypt(Span<byte> data)
{
for (var i = 0; i < data.Length; i++)
{
data[i] ^= this._xor3Keys[i % 3];
}
}
}