mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-01-27 22:27:58 +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.Net;
|
||||
using System.Net.Sockets;
|
||||
@ -180,23 +179,30 @@ namespace Syroot.Worms.Mgame.GameServer
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||
|
||||
protected override void OnPrePacketHandle(Packet packet)
|
||||
{
|
||||
_server.Log.Write(LogCategory.Client,
|
||||
$"{TcpClient.Client.RemoteEndPoint} >> {packet}{Environment.NewLine}{ObjectDumper.Dump(packet)}");
|
||||
_server.Log.Write(LogCategory.Client, FormatPacket(packet, ">>"));
|
||||
}
|
||||
|
||||
protected override void OnPrePacketSend(Packet packet)
|
||||
{
|
||||
_server.Log.Write(LogCategory.Server,
|
||||
$"{TcpClient.Client.RemoteEndPoint} << {packet}{Environment.NewLine}{ObjectDumper.Dump(packet)}");
|
||||
_server.Log.Write(LogCategory.Server, FormatPacket(packet, "<<"));
|
||||
if (_server.Config.SendDelay > 0)
|
||||
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)
|
||||
{
|
||||
TcpClient.Close();
|
||||
TcpClient.Dispose();
|
||||
_packetDataStream.Dispose();
|
||||
}
|
||||
|
||||
@ -205,8 +205,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
// Instantiate, deserialize, and return packet.
|
||||
int id = 0;
|
||||
Packet packet = PacketFactory.Create(PacketFormat.Wwpa, id);
|
||||
byte[] decompressedData = PacketCompression.Decompress(compressedData);
|
||||
using (PacketDataStream stream = new PacketDataStream(decompressedData))
|
||||
ReadOnlySpan<byte> decompressedData = PacketCompression.Decompress(compressedData);
|
||||
using (PacketDataStream stream = new PacketDataStream(decompressedData.ToArray()))
|
||||
packet.LoadData(stream);
|
||||
return packet;
|
||||
}
|
||||
|
@ -2,35 +2,29 @@
|
||||
|
||||
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 -------------------------------------------------------------------------------------------------
|
||||
|
||||
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 int _bufferCursor = 0;
|
||||
private static int _cursor = 0;
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed)
|
||||
public static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed)
|
||||
{
|
||||
Compressor compressor = new Compressor();
|
||||
|
||||
int idx = 0;
|
||||
while (idx < decompressed.Length)
|
||||
{
|
||||
int value1 = 0;
|
||||
int value2 = -1;
|
||||
int bytesToRepeat = Math.Max(0, idx - 0xFF);
|
||||
int idxDword = 0;
|
||||
int shiftValue1 = -1;
|
||||
if (bytesToRepeat >= idx)
|
||||
{
|
||||
Write(compressor, decompressed[idx++]);
|
||||
@ -42,45 +36,37 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||
int i;
|
||||
for (i = idx; i < decompressed.Length; i++)
|
||||
{
|
||||
if (i - idx >= 0x111)
|
||||
break;
|
||||
if (decompressed[bytesToRepeat] != decompressed[i])
|
||||
if (i - idx >= 0x111
|
||||
|| decompressed[bytesToRepeat] != decompressed[i])
|
||||
break;
|
||||
bytesToRepeat++;
|
||||
}
|
||||
int offset = idx - i;
|
||||
int v11 = i - idx;
|
||||
int v12 = offset + bytesToRepeat;
|
||||
if (v11 >= 3 && v11 > idxDword)
|
||||
int offset = i - idx;
|
||||
if (offset >= 3 && offset > value1)
|
||||
{
|
||||
idxDword = v11;
|
||||
shiftValue1 = idx - 12;
|
||||
if (v11 == 0x111)
|
||||
value1 = offset;
|
||||
value2 = idx - 12;
|
||||
if (offset == 0x111)
|
||||
break;
|
||||
}
|
||||
bytesToRepeat = v12 + 1;
|
||||
bytesToRepeat += idx - i + 1;
|
||||
} while (bytesToRepeat < idx);
|
||||
if (idxDword != 0)
|
||||
if (value1 != 0)
|
||||
{
|
||||
TransferBuffer(compressor);
|
||||
compressor.Compress(true);
|
||||
if (idxDword >= 18)
|
||||
if (value1 >= 18)
|
||||
{
|
||||
compressor.Shift(0, 4);
|
||||
compressor.Shift(shiftValue1, 8);
|
||||
compressor.Shift(idxDword - 18, 8);
|
||||
_field_C++;
|
||||
_field_10 = idxDword - 3 + _field_10;
|
||||
compressor.Shift(value2, 8);
|
||||
compressor.Shift(value1 - 18, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
compressor.Shift(idxDword - 2, 4);
|
||||
compressor.Shift(shiftValue1 - 1, 8);
|
||||
_field_4++;
|
||||
_field_8 = idxDword - 2 + _field_8;
|
||||
compressor.Shift(value1 - 2, 4);
|
||||
compressor.Shift(value2 - 1, 8);
|
||||
}
|
||||
idx += idxDword;
|
||||
_bufferDwords[idxDword]++;
|
||||
idx += value1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -96,30 +82,84 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||
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) --------------------------------------------------------------------------------------
|
||||
|
||||
private static void TransferBuffer(Compressor compressor)
|
||||
{
|
||||
if (_bufferCursor != 0)
|
||||
if (_cursor != 0)
|
||||
{
|
||||
_field_0 += _bufferCursor;
|
||||
compressor.Compress(false);
|
||||
compressor.Shift(_bufferCursor - 1, 8);
|
||||
for (int i = 0; i < _bufferCursor; i++)
|
||||
compressor.Shift(_cursor - 1, 8);
|
||||
for (int i = 0; i < _cursor; i++)
|
||||
compressor.Buffer[compressor.Cursor++] = _buffer[i];
|
||||
_bufferCursor = 0;
|
||||
_cursor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Write(Compressor compressor, byte c)
|
||||
{
|
||||
_buffer[_bufferCursor++] = c;
|
||||
if (_bufferCursor >= 256)
|
||||
_buffer[_cursor++] = c;
|
||||
if (_cursor >= _buffer.Length)
|
||||
TransferBuffer(compressor);
|
||||
}
|
||||
|
||||
@ -133,12 +173,10 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||
private int _cursorCompress = 0;
|
||||
private int _shift = 0;
|
||||
|
||||
internal void Compress(bool bShift)
|
||||
internal void Compress(bool doShift)
|
||||
{
|
||||
if (bShift)
|
||||
{
|
||||
if (doShift)
|
||||
Buffer[_cursorCompress] |= (byte)(0x80 >> _shift);
|
||||
}
|
||||
if (++_shift == 8)
|
||||
{
|
||||
_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.Diagnostics;
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
@ -14,12 +11,6 @@ namespace Syroot.Worms.Mgame.GameServer
|
||||
|
||||
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
|
||||
{
|
||||
// Start a new server.
|
||||
|
@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AssemblyName>Server</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.Mgame;
|
||||
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||
|
||||
namespace Syroot.Worms.Scratchpad
|
||||
{
|
||||
@ -11,8 +14,19 @@ namespace Syroot.Worms.Scratchpad
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
ConvertIgdImages();
|
||||
Console.ReadLine();
|
||||
DecompressWwpaPacketData();
|
||||
}
|
||||
|
||||
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()
|
||||
|
@ -3,6 +3,9 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Syroot.BinaryData" Version="5.1.0-beta1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user