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}");
                    }
                }
            }
        }