private void Generic_CanDeserialize_CaptureTest <TBasePayloadType>(PacketCaptureTestEntry entry) where TBasePayloadType : IPacketPayload, IOperationCodeable { //arrange SerializerService serializer = Serializer; TBasePayloadType payload; //act try { payload = serializer.Deserialize <TBasePayloadType>(entry.BinaryData); if (payload is IUnknownPayloadType) { Assert.Warn($"Encountered unimplemented OpCode: 0x{payload.OperationCode:X}."); return; } } catch (Exception e) { Assert.Fail($"Critical failure. Cannot deserialize File: {entry.FileName} FileSize: {entry.BinaryData.Length} \n\n Exception: {e.Message} Stack: {e.StackTrace}"); return; } finally { } //assert Assert.NotNull(payload, $"Resulting capture capture deserialization attempt null for File: {entry.FileName}"); //We should have deserialized it. We want to make sure the opcode matches Assert.AreEqual(entry.OpCode, payload.OperationCode, $"Mismatched {nameof(payload.OperationCode)} on packet capture File: {entry.FileName}. Expected: {entry.OpCode} Was: {payload.OperationCode}"); }
public void Can_Deserialize_ClientCaptures_To_ClientPayloads(PacketCaptureTestEntry entry) { Generic_CanDeserialize_CaptureTest <PSOBBGamePacketPayloadClient, GameNetworkOperationCode>(entry); }
public void Can_Serialize_DeserializedServerDTO_To_Same_Binary_Representation(PacketCaptureTestEntry entry) { Generic_Can_Serialize_DeserializedClientDTO_To_Same_Binary_Representation <PSOBBGamePacketPayloadServer, GameNetworkOperationCode>(entry); }
public void Generic_Can_Serialize_DeserializedClientDTO_To_Same_Binary_Representation <TBasePayloadType, TOperationCodeType>(PacketCaptureTestEntry entry) where TBasePayloadType : IOperationCodeable <TOperationCodeType>, ITypeSerializerReadingStrategy <TBasePayloadType>, ITypeSerializerWritingStrategy <TBasePayloadType> where TOperationCodeType : Enum { //arrange Console.WriteLine($"Entry Size: {entry.BinaryData.Length} OpCode: {entry.OpCode}"); SerializerService serializer = Serializer; TBasePayloadType payload = default; int readOffset = 0; try { payload = serializer.Read <TBasePayloadType>(entry.BinaryData, ref readOffset); } catch (IndexOutOfRangeException e) { Console.WriteLine($"Offset: {readOffset} BinarySize: {entry.BinaryData.Length}"); throw; } catch (Exception e) { Console.WriteLine($"Error: {e}"); throw; } if (payload is IUnknownPayloadType) { Assert.Warn($"Encountered unimplemented OpCode: 0x{payload.OperationCode:X} - {payload.OperationCode.ToString()}."); return; } bool isSub60 = false; bool isSub62 = false; //Special handling for 0x60 payloads, we don't want to deal with unknown subcommands. Just warn if (payload is ISub60CommandContainer sub60) { isSub60 = true; if (sub60.Command is UnknownSubCommand60Command) { Assert.Warn($"Encountered unimplemented Sub60 SubCommand: 0x{payload.OperationCode:X} 0x{entry.BinaryData[6]:X}."); return; } } else if (payload is ISub62CommandContainer sub62) { isSub62 = true; if (sub62.Command is UnknownSubCommand62Command) { Assert.Warn($"Encountered unimplemented Sub62 SubCommand: 0x{payload.OperationCode:X} 0x{entry.BinaryData[6]:X}."); return; } } //act byte[] rentedBuffer = ArrayPool <byte> .Shared.Rent(36000 * 2); //Must zero out for cleanest tests. for (int i = 0; i < rentedBuffer.Length; i++) { rentedBuffer[i] = default(byte); } try { Span <byte> buffer = new Span <byte>(rentedBuffer); int offset = 0; serializer.Write(payload, buffer, ref offset); buffer = buffer.Slice(0, offset); //The plus 2 is from the packet size which is included as calculation in whether it is appropriate for the block //size of 8. //+2 is also added to the original binary length since it's missing the packet size too. int serializedBytesWithBlockSize = ConvertToBlocksizeCount(buffer.Length + 2, 8); int entryBytesWithBlockSize = ConvertToBlocksizeCount(entry.BinaryData.Length + 2, 8); //assert try { if (!isSub60 && !isSub62) { //convert the serialized bytes to block size since Sylverant and the packet captures will include that in the data Assert.AreEqual(entryBytesWithBlockSize, serializedBytesWithBlockSize, $"Mismatched length on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name}"); } else if (isSub60) { var command = (payload as ISub60CommandContainer).Command; //Similar to the above but we include information about the sub60 command Assert.AreEqual(entryBytesWithBlockSize, serializedBytesWithBlockSize, $"Mismatched length on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); //Command 60s should also check the command size Assert.AreEqual(entry.BinaryData[7], buffer[7], $"Mismatched Sub60 {nameof(BaseSubCommand60.CommandSize)} on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } else if (isSub62) { var command = (payload as ISub62CommandContainer).Command; //Similar to the above but we include information about the sub60 command Assert.AreEqual(entryBytesWithBlockSize, serializedBytesWithBlockSize, $"Mismatched length on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub62 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } } catch (AssertionException e) { Assert.Fail($"Failed: {e.Message} {PrintFailureBytes(entry.BinaryData, buffer)}"); } //check both lengths since we accept that some packet models won't include the padding. for (int i = 0; i < entry.BinaryData.Length && i < buffer.Length; i++) { if (!isSub60 && !isSub62) { //This is an optimization to avoid pointless string allocations/building. if (entry.BinaryData[i] != buffer[i]) { Assert.AreEqual(entry.BinaryData[i], buffer[i], $"Mismatched byte value at Index: {i} on OpCode: 0x{entry.OpCode:X} Type: {payload.GetType().Name} {PrintFailureBytes(entry.BinaryData, buffer)}"); } } else if (isSub60) { var command = (payload as ISub60CommandContainer).Command; //This is an optimization to avoid pointless string allocations/building. if (entry.BinaryData[i] != buffer[i]) { Assert.AreEqual(entry.BinaryData[i], buffer[i], $"Mismatched byte value at Index: {i} on OpCode: 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name} {PrintFailureBytes(entry.BinaryData, buffer)}"); } } else if (isSub62) { var command = (payload as ISub62CommandContainer).Command; //This is an optimization to avoid pointless string allocations/building. if (entry.BinaryData[i] != buffer[i]) { Assert.AreEqual(entry.BinaryData[i], buffer[i], $"Mismatched byte value at Index: {i} on OpCode: 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub62 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name} {PrintFailureBytes(entry.BinaryData, buffer)}"); } } } //Special check for when we have a packet that hasn't failed but has differently lenghts (assumed to be blocksize) //we check and make sure that the ending bytes are actually 0. If they aren't it likely NOT padding and additional unhandled data if (entry.BinaryData.Length > buffer.Length) { for (int i = buffer.Length; i < entry.BinaryData.Length; i++) { if (!isSub60 && !isSub62) { Assert.AreEqual(0, entry.BinaryData[i], $"Encountered assumed padding byte at Index: {i} on OpCode: 0x{entry.OpCode} Type: {payload.GetType().Name} but value was: 0x{entry.BinaryData[i]:X}"); } else if (isSub60) { var command = (payload as ISub60CommandContainer).Command; Assert.AreEqual(0, entry.BinaryData[i], $"Encountered assumed padding byte at Index: {i} on OpCode: 0x{entry.OpCode} Type: {payload.GetType().Name} but value was: 0x{entry.BinaryData[i]:X} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } else if (isSub62) { var command = (payload as ISub62CommandContainer).Command; Assert.AreEqual(0, entry.BinaryData[i], $"Encountered assumed padding byte at Index: {i} on OpCode: 0x{entry.OpCode} Type: {payload.GetType().Name} but value was: 0x{entry.BinaryData[i]:X} Sub62 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } } } } finally { ArrayPool <byte> .Shared.Return(rentedBuffer); } }
public static void Can_Deserialize_Captures_To_GamePacketPayloads(PacketCaptureTestEntry entry) { Console.WriteLine($"Entry Size: {entry.BinaryData.Length} OpCode: {entry.OpCode}"); //TODO: Test compression another time. if (entry.OpCode == NetworkOperationCode.SMSG_COMPRESSED_UPDATE_OBJECT) { //Skip the opcode int decompressedSize = entry.BinaryData.Reinterpret <int>(2); byte[] newBytes = new byte[decompressedSize + 2]; // +2 for opcode ZlibCodec stream = new ZlibCodec(CompressionMode.Decompress) { InputBuffer = entry.BinaryData, NextIn = 2 + 4, //opcode + size AvailableBytesIn = entry.BinaryData.Length, OutputBuffer = newBytes, NextOut = 2, AvailableBytesOut = decompressedSize }; stream.Inflate(FlushType.None); stream.Inflate(FlushType.Finish); stream.EndInflate(); ((short)(NetworkOperationCode.SMSG_UPDATE_OBJECT)).Reinterpret(newBytes, 0); entry = new PacketCaptureTestEntry(NetworkOperationCode.SMSG_UPDATE_OBJECT, newBytes, entry.FileName); } //arrange SerializerService serializer = Serializer; GamePacketPayload payload; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); //act try { payload = serializer.Deserialize <GamePacketPayload>(entry.BinaryData); } catch (Exception e) { Console.WriteLine($"Critical failure. Cannot deserialize File: {entry.FileName} FileSize: {entry.BinaryData.Length} \n\n Exception: {e.Message} Stack: {e.StackTrace}"); return; } finally { stopwatch.Stop(); } Console.WriteLine($"Serialization time in ms: {stopwatch.ElapsedMilliseconds}"); foreach (ObjectUpdateBlock block in ((IObjectUpdatePayload)payload).UpdateBlocks.Items) { Console.WriteLine($"Encountered: {block.GetType().Name} Block Type: {block.UpdateType}"); } //assert if (payload == null) { Console.WriteLine($"Resulting capture capture deserialization attempt null for File: {entry.FileName}"); } //We should have deserialized it. We want to make sure the opcode matches if (entry.OpCode != payload.OperationCode) { Console.WriteLine($"Mismatched {nameof(NetworkOperationCode)} on packet capture File: {entry.FileName}. Expected: {entry.OpCode} Was: {payload.OperationCode}"); } }
public void Can_Deserialize_ClientCaptures_To_ClientPayloads(PacketCaptureTestEntry entry) { Generic_CanDeserialize_CaptureTest <PSOBBGamePacketPayloadClient>(entry); }
public void Can_Serialize_DeserializedClientDTO_To_Same_Binary_Representation(PacketCaptureTestEntry entry) { Generic_Can_Serialize_DeserializedClientDTO_To_Same_Binary_Representation <PSOBBGamePacketPayloadClient>(entry); }
public void Generic_Can_Serialize_DeserializedClientDTO_To_Same_Binary_Representation <TBasePayloadType>(PacketCaptureTestEntry entry) where TBasePayloadType : IPacketPayload, IOperationCodeable { //arrange Console.WriteLine($"Entry Size: {entry.BinaryData.Length} OpCode: {entry.OpCode}"); SerializerService serializer = Serializer; TBasePayloadType payload = serializer.Deserialize <TBasePayloadType>(entry.BinaryData); if (payload is IUnknownPayloadType) { Assert.Warn($"Encountered unimplemented OpCode: 0x{payload.OperationCode:X} - {(GameNetworkOperationCode)payload.OperationCode}."); return; } bool isSub60 = false; bool isSub62 = false; //Special handling for 0x60 payloads, we don't want to deal with unknown subcommands. Just warn if (payload is ISub60CommandContainer sub60) { isSub60 = true; if (sub60.Command is UnknownSubCommand60Command) { Assert.Warn($"Encountered unimplemented Sub60 SubCommand: 0x{payload.OperationCode:X} 0x{entry.BinaryData[6]:X}."); return; } } else if (payload is ISub62CommandContainer sub62) { isSub62 = true; if (sub62.Command is UnknownSubCommand62Command) { Assert.Warn($"Encountered unimplemented Sub62 SubCommand: 0x{payload.OperationCode:X} 0x{entry.BinaryData[6]:X}."); return; } } //act byte[] serializedBytes = serializer.Serialize(payload); //The plus 2 is from the packet size which is included as calculation in whether it is appropriate for the block //size of 8. //+2 is also added to the original binary length since it's missing the packet size too. int serializedBytesWithBlockSize = ConvertToBlocksizeCount(serializedBytes.Length + 2, 8); int entryBytesWithBlockSize = ConvertToBlocksizeCount(entry.BinaryData.Length + 2, 8); //assert try { if (!isSub60 && !isSub62) { //convert the serialized bytes to block size since Sylverant and the packet captures will include that in the data Assert.AreEqual(entryBytesWithBlockSize, serializedBytesWithBlockSize, $"Mismatched length on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name}"); } else if (isSub60) { var command = (payload as ISub60CommandContainer).Command; //Similar to the above but we include information about the sub60 command Assert.AreEqual(entryBytesWithBlockSize, serializedBytesWithBlockSize, $"Mismatched length on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); //Command 60s should also check the command size Assert.AreEqual(entry.BinaryData[7], serializedBytes[7], $"Mismatched Sub60 {nameof(BaseSubCommand60.CommandSize)} on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } else if (isSub62) { var command = (payload as ISub62CommandContainer).Command; //Similar to the above but we include information about the sub60 command Assert.AreEqual(entryBytesWithBlockSize, serializedBytesWithBlockSize, $"Mismatched length on OpCode: {(GameNetworkOperationCode)entry.OpCode} - 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub62 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } } catch (AssertionException e) { Assert.Fail($"Failed: {e.Message} {PrintFailureBytes(entry.BinaryData, serializedBytes)}"); } //check both lengths since we accept that some packet models won't include the padding. for (int i = 0; i < entry.BinaryData.Length && i < serializedBytes.Length; i++) { if (!isSub60 && !isSub62) { Assert.AreEqual(entry.BinaryData[i], serializedBytes[i], $"Mismatched byte value at Index: {i} on OpCode: 0x{entry.OpCode:X} Type: {payload.GetType().Name}"); } else if (isSub60) { var command = (payload as ISub60CommandContainer).Command; Assert.AreEqual(entry.BinaryData[i], serializedBytes[i], $"Mismatched byte value at Index: {i} on OpCode: 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } else if (isSub62) { var command = (payload as ISub62CommandContainer).Command; Assert.AreEqual(entry.BinaryData[i], serializedBytes[i], $"Mismatched byte value at Index: {i} on OpCode: 0x{entry.OpCode:X} Type: {payload.GetType().Name} Sub62 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } } //Special check for when we have a packet that hasn't failed but has differently lenghts (assumed to be blocksize) //we check and make sure that the ending bytes are actually 0. If they aren't it likely NOT padding and additional unhandled data if (entry.BinaryData.Length > serializedBytes.Length) { for (int i = serializedBytes.Length; i < entry.BinaryData.Length; i++) { if (!isSub60 && !isSub62) { Assert.AreEqual(0, entry.BinaryData[i], $"Encountered assumed padding byte at Index: {i} on OpCode: 0x{entry.OpCode} Type: {payload.GetType().Name} but value was: 0x{entry.BinaryData[i]:X}"); } else if (isSub60) { var command = (payload as ISub60CommandContainer).Command; Assert.AreEqual(0, entry.BinaryData[i], $"Encountered assumed padding byte at Index: {i} on OpCode: 0x{entry.OpCode} Type: {payload.GetType().Name} but value was: 0x{entry.BinaryData[i]:X} Sub60 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } else if (isSub62) { var command = (payload as ISub62CommandContainer).Command; Assert.AreEqual(0, entry.BinaryData[i], $"Encountered assumed padding byte at Index: {i} on OpCode: 0x{entry.OpCode} Type: {payload.GetType().Name} but value was: 0x{entry.BinaryData[i]:X} Sub62 OpCode: 0x{entry.BinaryData[6]:X} Type: {command.GetType().Name}"); } } } }