mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-05-05 19:29:34 +03:00
Port WWPA packet decompression to C#.
This commit is contained in:
parent
efc1a9fcff
commit
197a958a98
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
@ -180,23 +179,30 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public void HandleRaw(RawPacket packet) { }
|
public void HandleRaw(RawPacket packet)
|
||||||
|
{
|
||||||
|
SendPacket(new RawPacket(PacketFormat.Wwpa, 0x0000,
|
||||||
|
new byte[] { 0x01, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00 }));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
protected override void OnPrePacketHandle(Packet packet)
|
protected override void OnPrePacketHandle(Packet packet)
|
||||||
{
|
{
|
||||||
_server.Log.Write(LogCategory.Client,
|
_server.Log.Write(LogCategory.Client, FormatPacket(packet, ">>"));
|
||||||
$"{TcpClient.Client.RemoteEndPoint} >> {packet}{Environment.NewLine}{ObjectDumper.Dump(packet)}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPrePacketSend(Packet packet)
|
protected override void OnPrePacketSend(Packet packet)
|
||||||
{
|
{
|
||||||
_server.Log.Write(LogCategory.Server,
|
_server.Log.Write(LogCategory.Server, FormatPacket(packet, "<<"));
|
||||||
$"{TcpClient.Client.RemoteEndPoint} << {packet}{Environment.NewLine}{ObjectDumper.Dump(packet)}");
|
|
||||||
if (_server.Config.SendDelay > 0)
|
if (_server.Config.SendDelay > 0)
|
||||||
Thread.Sleep(_server.Config.SendDelay);
|
Thread.Sleep(_server.Config.SendDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private string FormatPacket(Packet packet, string direction)
|
||||||
|
=> $"{TcpClient.Client.RemoteEndPoint} {direction} {packet.GetType().Name}{ObjectDumper.Dump(packet)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -81,7 +81,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
TcpClient.Close();
|
TcpClient.Dispose();
|
||||||
_packetDataStream.Dispose();
|
_packetDataStream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +205,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
|
|||||||
// Instantiate, deserialize, and return packet.
|
// Instantiate, deserialize, and return packet.
|
||||||
int id = 0;
|
int id = 0;
|
||||||
Packet packet = PacketFactory.Create(PacketFormat.Wwpa, id);
|
Packet packet = PacketFactory.Create(PacketFormat.Wwpa, id);
|
||||||
byte[] decompressedData = PacketCompression.Decompress(compressedData);
|
ReadOnlySpan<byte> decompressedData = PacketCompression.Decompress(compressedData);
|
||||||
using (PacketDataStream stream = new PacketDataStream(decompressedData))
|
using (PacketDataStream stream = new PacketDataStream(decompressedData.ToArray()))
|
||||||
packet.LoadData(stream);
|
packet.LoadData(stream);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
@ -2,35 +2,29 @@
|
|||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||||
{
|
{
|
||||||
internal static class PacketCompression
|
/// <summary>
|
||||||
|
/// Represents packet data encryption as used in WWPA. Note that most code is a direct translation from the original
|
||||||
|
/// game assembly.
|
||||||
|
/// </summary>
|
||||||
|
public static class PacketCompression
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private static int _field_0 = 0;
|
|
||||||
|
|
||||||
private static int _field_4 = 4;
|
|
||||||
private static int _field_8 = 0;
|
|
||||||
|
|
||||||
private static int _field_C = 0;
|
|
||||||
private static int _field_10 = 0;
|
|
||||||
|
|
||||||
private static int[] _bufferDwords = new int[512];
|
|
||||||
|
|
||||||
private static byte[] _buffer = new byte[256];
|
private static byte[] _buffer = new byte[256];
|
||||||
private static int _bufferCursor = 0;
|
private static int _cursor = 0;
|
||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed)
|
public static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed)
|
||||||
{
|
{
|
||||||
Compressor compressor = new Compressor();
|
Compressor compressor = new Compressor();
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
while (idx < decompressed.Length)
|
while (idx < decompressed.Length)
|
||||||
{
|
{
|
||||||
|
int value1 = 0;
|
||||||
|
int value2 = -1;
|
||||||
int bytesToRepeat = Math.Max(0, idx - 0xFF);
|
int bytesToRepeat = Math.Max(0, idx - 0xFF);
|
||||||
int idxDword = 0;
|
|
||||||
int shiftValue1 = -1;
|
|
||||||
if (bytesToRepeat >= idx)
|
if (bytesToRepeat >= idx)
|
||||||
{
|
{
|
||||||
Write(compressor, decompressed[idx++]);
|
Write(compressor, decompressed[idx++]);
|
||||||
@ -42,45 +36,37 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
|||||||
int i;
|
int i;
|
||||||
for (i = idx; i < decompressed.Length; i++)
|
for (i = idx; i < decompressed.Length; i++)
|
||||||
{
|
{
|
||||||
if (i - idx >= 0x111)
|
if (i - idx >= 0x111
|
||||||
break;
|
|| decompressed[bytesToRepeat] != decompressed[i])
|
||||||
if (decompressed[bytesToRepeat] != decompressed[i])
|
|
||||||
break;
|
break;
|
||||||
bytesToRepeat++;
|
bytesToRepeat++;
|
||||||
}
|
}
|
||||||
int offset = idx - i;
|
int offset = i - idx;
|
||||||
int v11 = i - idx;
|
if (offset >= 3 && offset > value1)
|
||||||
int v12 = offset + bytesToRepeat;
|
|
||||||
if (v11 >= 3 && v11 > idxDword)
|
|
||||||
{
|
{
|
||||||
idxDword = v11;
|
value1 = offset;
|
||||||
shiftValue1 = idx - 12;
|
value2 = idx - 12;
|
||||||
if (v11 == 0x111)
|
if (offset == 0x111)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bytesToRepeat = v12 + 1;
|
bytesToRepeat += idx - i + 1;
|
||||||
} while (bytesToRepeat < idx);
|
} while (bytesToRepeat < idx);
|
||||||
if (idxDword != 0)
|
if (value1 != 0)
|
||||||
{
|
{
|
||||||
TransferBuffer(compressor);
|
TransferBuffer(compressor);
|
||||||
compressor.Compress(true);
|
compressor.Compress(true);
|
||||||
if (idxDword >= 18)
|
if (value1 >= 18)
|
||||||
{
|
{
|
||||||
compressor.Shift(0, 4);
|
compressor.Shift(0, 4);
|
||||||
compressor.Shift(shiftValue1, 8);
|
compressor.Shift(value2, 8);
|
||||||
compressor.Shift(idxDword - 18, 8);
|
compressor.Shift(value1 - 18, 8);
|
||||||
_field_C++;
|
|
||||||
_field_10 = idxDword - 3 + _field_10;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
compressor.Shift(idxDword - 2, 4);
|
compressor.Shift(value1 - 2, 4);
|
||||||
compressor.Shift(shiftValue1 - 1, 8);
|
compressor.Shift(value2 - 1, 8);
|
||||||
_field_4++;
|
|
||||||
_field_8 = idxDword - 2 + _field_8;
|
|
||||||
}
|
}
|
||||||
idx += idxDword;
|
idx += value1;
|
||||||
_bufferDwords[idxDword]++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -96,30 +82,84 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
|||||||
return compressor.Buffer.AsSpan(0, compressor.Cursor);
|
return compressor.Buffer.AsSpan(0, compressor.Cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static byte[] Decompress(ReadOnlySpan<byte> compressed)
|
public static ReadOnlySpan<byte> Decompress(ReadOnlySpan<byte> compressed)
|
||||||
{
|
{
|
||||||
return new byte[1];
|
Decompressor decompressor = new Decompressor(compressed);
|
||||||
|
byte[] decompressed = new byte[2048];
|
||||||
|
|
||||||
|
int bytesToCopy;
|
||||||
|
int bytesToCopyRemain;
|
||||||
|
int idx = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
while (decompressor.sub_4C0BA0() == 0)
|
||||||
|
{
|
||||||
|
bytesToCopy = decompressor.GetBytesToCopy(8);
|
||||||
|
if (bytesToCopy != -1)
|
||||||
|
{
|
||||||
|
bytesToCopy++;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
decompressed[idx++] = decompressor.Read();
|
||||||
|
bytesToCopy--;
|
||||||
|
} while (bytesToCopy != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decompressor.GetBytesToCopy(4) + 2 <= 2)
|
||||||
|
break;
|
||||||
|
bytesToCopy = decompressor.GetBytesToCopy(8) + 1;
|
||||||
|
bytesToCopyRemain = bytesToCopy - 1;
|
||||||
|
if (bytesToCopy != 0)
|
||||||
|
{
|
||||||
|
bytesToCopy++;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int idxCopySrc = idx++ - bytesToCopy;
|
||||||
|
bytesToCopyRemain--;
|
||||||
|
decompressed[idx - 1] = decompressed[idxCopySrc];
|
||||||
|
} while (bytesToCopyRemain != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytesToCopy = decompressor.GetBytesToCopy(8);
|
||||||
|
if (bytesToCopy == 0)
|
||||||
|
break;
|
||||||
|
int v12 = decompressor.GetBytesToCopy(8);
|
||||||
|
int v13 = v12 + 18;
|
||||||
|
bytesToCopyRemain = v12 + 17;
|
||||||
|
if (v13 != 0)
|
||||||
|
{
|
||||||
|
bytesToCopyRemain++;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int idxCopySrc = idx++ - bytesToCopy;
|
||||||
|
bytesToCopyRemain--;
|
||||||
|
decompressed[idx - 1] = decompressed[idxCopySrc];
|
||||||
|
} while (bytesToCopyRemain != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decompressed.AsSpan(0, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private static void TransferBuffer(Compressor compressor)
|
private static void TransferBuffer(Compressor compressor)
|
||||||
{
|
{
|
||||||
if (_bufferCursor != 0)
|
if (_cursor != 0)
|
||||||
{
|
{
|
||||||
_field_0 += _bufferCursor;
|
|
||||||
compressor.Compress(false);
|
compressor.Compress(false);
|
||||||
compressor.Shift(_bufferCursor - 1, 8);
|
compressor.Shift(_cursor - 1, 8);
|
||||||
for (int i = 0; i < _bufferCursor; i++)
|
for (int i = 0; i < _cursor; i++)
|
||||||
compressor.Buffer[compressor.Cursor++] = _buffer[i];
|
compressor.Buffer[compressor.Cursor++] = _buffer[i];
|
||||||
_bufferCursor = 0;
|
_cursor = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Write(Compressor compressor, byte c)
|
private static void Write(Compressor compressor, byte c)
|
||||||
{
|
{
|
||||||
_buffer[_bufferCursor++] = c;
|
_buffer[_cursor++] = c;
|
||||||
if (_bufferCursor >= 256)
|
if (_cursor >= _buffer.Length)
|
||||||
TransferBuffer(compressor);
|
TransferBuffer(compressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,12 +173,10 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
|||||||
private int _cursorCompress = 0;
|
private int _cursorCompress = 0;
|
||||||
private int _shift = 0;
|
private int _shift = 0;
|
||||||
|
|
||||||
internal void Compress(bool bShift)
|
internal void Compress(bool doShift)
|
||||||
{
|
{
|
||||||
if (bShift)
|
if (doShift)
|
||||||
{
|
|
||||||
Buffer[_cursorCompress] |= (byte)(0x80 >> _shift);
|
Buffer[_cursorCompress] |= (byte)(0x80 >> _shift);
|
||||||
}
|
|
||||||
if (++_shift == 8)
|
if (++_shift == 8)
|
||||||
{
|
{
|
||||||
_cursorCompress = Cursor++;
|
_cursorCompress = Cursor++;
|
||||||
@ -162,5 +200,52 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class Decompressor
|
||||||
|
{
|
||||||
|
private byte[] _compressed;
|
||||||
|
private int _compressedCursor = 0;
|
||||||
|
private byte _field_C;
|
||||||
|
private byte _field_D;
|
||||||
|
|
||||||
|
internal Decompressor(ReadOnlySpan<byte> compressed)
|
||||||
|
{
|
||||||
|
_compressed = compressed.ToArray();
|
||||||
|
byte c = Read();
|
||||||
|
_field_C = (byte)(2 * c | 1);
|
||||||
|
_field_D = (byte)(c >> 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte Read()
|
||||||
|
{
|
||||||
|
return _compressed[_compressedCursor++];
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetBytesToCopy(int numCalls)
|
||||||
|
{
|
||||||
|
if (numCalls == 0)
|
||||||
|
return 0;
|
||||||
|
int bytesToCopy = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
numCalls--;
|
||||||
|
bytesToCopy = sub_4C0BA0() + 2 * bytesToCopy;
|
||||||
|
} while (numCalls != 0);
|
||||||
|
return bytesToCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int sub_4C0BA0()
|
||||||
|
{
|
||||||
|
byte field_D = _field_D;
|
||||||
|
_field_D = (byte)(_field_C >> 7);
|
||||||
|
_field_C *= 2;
|
||||||
|
if (_field_C == 0)
|
||||||
|
{
|
||||||
|
byte c = Read();
|
||||||
|
_field_C = (byte)(2 * c | 1);
|
||||||
|
_field_D = (byte)(c >> 7);
|
||||||
|
}
|
||||||
|
return field_D;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using Syroot.BinaryData;
|
|
||||||
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer
|
namespace Syroot.Worms.Mgame.GameServer
|
||||||
{
|
{
|
||||||
@ -14,12 +11,6 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
|
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
byte[] decompressed = new byte[sizeof(int)];
|
|
||||||
ByteConverter.System.GetBytes(0x8001, decompressed);
|
|
||||||
ReadOnlySpan<byte> compressed = PacketCompression.Compress(decompressed);
|
|
||||||
Debug.Assert(compressed.SequenceEqual(new byte[] { 0x01, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00 }),
|
|
||||||
"Compression failed");
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Start a new server.
|
// Start a new server.
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<AssemblyName>Server</AssemblyName>
|
<AssemblyName>Server</AssemblyName>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Mgame;
|
using Syroot.Worms.Mgame;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||||
|
|
||||||
namespace Syroot.Worms.Scratchpad
|
namespace Syroot.Worms.Scratchpad
|
||||||
{
|
{
|
||||||
@ -11,8 +14,19 @@ namespace Syroot.Worms.Scratchpad
|
|||||||
|
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
ConvertIgdImages();
|
DecompressWwpaPacketData();
|
||||||
Console.ReadLine();
|
}
|
||||||
|
|
||||||
|
private static void DecompressWwpaPacketData()
|
||||||
|
{
|
||||||
|
byte[] decompressed = new byte[sizeof(int)];
|
||||||
|
ByteConverter.System.GetBytes(0x8001, decompressed);
|
||||||
|
ReadOnlySpan<byte> compressed = PacketCompression.Compress(decompressed);
|
||||||
|
Debug.Assert(compressed.SequenceEqual(new byte[] { 0x01, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00 }),
|
||||||
|
"Compression failed");
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> decompressedNew = PacketCompression.Decompress(compressed);
|
||||||
|
Debug.Assert(decompressedNew.SequenceEqual(decompressed), "Decompression failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConvertIgdImages()
|
private static void ConvertIgdImages()
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Syroot.BinaryData" Version="5.1.0-beta1" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" />
|
<ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" />
|
||||||
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user