diff --git a/WowPacketParser/Loading/IWritingStrategy.cs b/WowPacketParser/Loading/IWritingStrategy.cs new file mode 100644 index 0000000000..df228e32eb --- /dev/null +++ b/WowPacketParser/Loading/IWritingStrategy.cs @@ -0,0 +1,11 @@ +using System; + +namespace WowPacketParser.Loading +{ + public interface IWritingStrategy : IDisposable + { + void Write(object input); + + void Flush(); + } +} diff --git a/WowPacketParser/Loading/SniffFile.cs b/WowPacketParser/Loading/SniffFile.cs index 5f6ae5f474..ea67b4a863 100644 --- a/WowPacketParser/Loading/SniffFile.cs +++ b/WowPacketParser/Loading/SniffFile.cs @@ -28,6 +28,7 @@ public class SniffFile private readonly Statistics _stats; private readonly DumpFormatType _dumpFormat; private readonly string _logPrefix; + private static long _lastPercent; private readonly List _withErrorHeaders = new List(); private readonly List _skippedHeaders = new List(); @@ -79,7 +80,6 @@ private string FileName public void ProcessFile() { - try { ProcessFileImpl(); @@ -113,22 +113,7 @@ private void ProcessFileImpl() if (packets.Count == 0) break; - var firstPacket = packets.First(); - var lastPacket = packets.Last(); - - // CSV format - // ReSharper disable once UseStringInterpolation - Trace.WriteLine(string.Format("{0};{1};{2};{3};{4};{5};{6};{7};{8}", - FileName, // - sniff file name - firstPacket.Time, // - time of first packet - lastPacket.Time, // - time of last packet - (lastPacket.Time - firstPacket.Time).TotalSeconds, // - sniff duration (seconds) - packets.Count, // - packet count - packets.AsParallel().Sum(packet => packet.Length), // - total packets size (bytes) - packets.AsParallel().Average(packet => packet.Length), // - average packet size (bytes) - packets.AsParallel().Min(packet => packet.Length), // - smaller packet size (bytes) - packets.AsParallel().Max(packet => packet.Length))); // - larger packet size (bytes) - + StatsAnalysis(packets); break; } case DumpFormatType.SniffDataOnly: @@ -146,131 +131,7 @@ private void ProcessFileImpl() break; } - Store.Store.SQLEnabledFlags = Settings.SQLOutputFlag; - File.Delete(outFileName); - - _stats.SetStartTime(DateTime.Now); - - var threadCount = Settings.Threads; - if (threadCount == 0) - threadCount = Environment.ProcessorCount; - - ThreadPool.SetMinThreads(threadCount + 2, 4); - - var written = false; - using (var writer = (Settings.DumpFormatWithText() ? new StreamWriter(outFileName, true) : null)) - { - var firstRead = true; - var firstWrite = true; - - var reader = _compression != FileCompression.None ? new Reader(_tempName, _sniffType) : new Reader(FileName, _sniffType); - - var pwp = new ParallelWorkProcessor(() => // read - { - if (!reader.PacketReader.CanRead()) - return Tuple.Create(null, true); - - Packet packet; - var b = reader.TryRead(out packet); - - if (firstRead) - { - Trace.WriteLine( - $"{_logPrefix}: Parsing {Utilities.BytesToString(reader.PacketReader.GetTotalSize())} of packets. Detected version {ClientVersion.VersionString}"); - - firstRead = false; - } - - return Tuple.Create(packet, b); - }, packet => // parse - { - // Parse the packet, adding text to Writer and stuff to the stores - if (packet.Direction == Direction.BNClientToServer || - packet.Direction == Direction.BNServerToClient) - BattlenetHandler.ParseBattlenet(packet); - else - Handler.Parse(packet); - - // Update statistics - _stats.AddByStatus(packet.Status); - return packet; - }, - packet => // write - { - ShowPercentProgress("Processing...", reader.PacketReader.GetCurrentSize(), reader.PacketReader.GetTotalSize()); - - if (!packet.Status.HasAnyFlag(Settings.OutputFlag) || !packet.WriteToFile) - { - packet.ClosePacket(); - return; - } - - written = true; - - if (firstWrite) - { - // ReSharper disable AccessToDisposedClosure - writer?.WriteLine(GetHeader(FileName)); - // ReSharper restore AccessToDisposedClosure - - firstWrite = false; - } - - // get packet header if necessary - if (Settings.LogPacketErrors) - { - switch (packet.Status) - { - case ParsedStatus.WithErrors: - _withErrorHeaders.Add(packet.GetHeader()); - break; - case ParsedStatus.NotParsed: - _skippedHeaders.Add(packet.GetHeader()); - break; - case ParsedStatus.NoStructure: - _noStructureHeaders.Add(packet.GetHeader()); - break; - } - } - -// ReSharper disable AccessToDisposedClosure - if (writer != null) - { - // Write to file - writer.WriteLine(packet.Writer); - writer.Flush(); - } -// ReSharper restore AccessToDisposedClosure - - // Close Writer, Stream - Dispose - packet.ClosePacket(); - }, threadCount); - - pwp.WaitForFinished(Timeout.Infinite); - - reader.PacketReader.Dispose(); - - _stats.SetEndTime(DateTime.Now); - } - - if (written) - Trace.WriteLine($"{_logPrefix}: Saved file to '{outFileName}'"); - else - { - Trace.WriteLine($"{_logPrefix}: No file produced"); - File.Delete(outFileName); - } - - Trace.WriteLine($"{_logPrefix}: {_stats}"); - - if (Settings.SQLOutputFlag != 0 || HotfixSettings.Instance.ShouldLog()) - WriteSQLs(); - - if (Settings.LogPacketErrors) - WritePacketErrors(); - - GC.Collect(); // Force a GC collect after parsing a file. It seems to help. - + LaunchProcessing(outFileName); break; } case DumpFormatType.Pkt: @@ -279,29 +140,7 @@ private void ProcessFileImpl() if (packets.Count == 0) break; - if (Settings.FilterPacketsNum < 0) - { - var packetsPerSplit = Math.Abs(Settings.FilterPacketsNum); - var totalPackets = packets.Count; - - var numberOfSplits = (int)Math.Ceiling((double)totalPackets/packetsPerSplit); - - for (var i = 0; i < numberOfSplits; ++i) - { - var fileNamePart = FileName + "_part_" + (i + 1) + ".pkt"; - - var packetsPart = packets.Take(packetsPerSplit).ToList(); - packets.RemoveRange(0, packetsPart.Count); - - BinaryDump(fileNamePart, packetsPart); - } - } - else - { - var fileNameExcerpt = Path.ChangeExtension(FileName, null) + "_excerpt.pkt"; - BinaryDump(fileNameExcerpt, packets); - } - + ProcessPacket(packets); break; } case DumpFormatType.PktSplit: @@ -344,88 +183,317 @@ private void ProcessFileImpl() } case DumpFormatType.SniffVersionSplit: { - var reader = _compression != FileCompression.None ? new Reader(_tempName, _sniffType) : new Reader(FileName, _sniffType); + ProcessVersion(); + break; + } + case DumpFormatType.ConnectionIndexes: + { + var packets = ReadPackets(); + if (packets.Count == 0) + break; + + ProcessIndexes(packets); + break; + } + case DumpFormatType.Fusion: + { + var packets = ReadPackets(); + if (packets.Count == 0) + break; - if (ClientVersion.IsUndefined() && reader.PacketReader.CanRead()) + FusionDump(packets); + break; + } + default: + { + Trace.WriteLine($"{_logPrefix}: Dump format is none, nothing will be processed."); + break; + } + } + } + + private void LaunchProcessing(string outFileName) + { + var threadCount = SetupThreads(outFileName); + + var written = false; + using (var writer = (Settings.DumpFormatWithText() ? GetWriter(outFileName) : null)) + { + var firstRead = true; + var firstWrite = true; + + var reader = _compression != FileCompression.None ? new Reader(_tempName, _sniffType) : new Reader(FileName, _sniffType); + + var pwp = new ParallelWorkProcessor(() => // read + { + var tuple = ReadingProcedure(reader); + + if (firstRead) + { + Trace.WriteLine( + $"{_logPrefix}: Parsing {Utilities.BytesToString(reader.PacketReader.GetTotalSize())} of packets. Detected version {ClientVersion.VersionString}"); + + firstRead = false; + } + + return tuple; + }, packet => // process + { + ProcessingProcedure(packet); + + return packet; + }, + packet => // write + { + ShowPercentProgress("Processing...", reader.PacketReader.GetCurrentSize(), reader.PacketReader.GetTotalSize()); + + if (!packet.Status.HasAnyFlag(Settings.OutputFlag) || !packet.WriteToFile) { - Packet packet; - reader.TryRead(out packet); packet.ClosePacket(); + return; } - reader.PacketReader.Dispose(); + written = true; - var version = ClientVersion.IsUndefined() ? "unknown" : ClientVersion.VersionString; + if (firstWrite) + { + // ReSharper disable AccessToDisposedClosure + writer?.Write(GetHeader(FileName)); + // ReSharper restore AccessToDisposedClosure + firstWrite = false; + } - var realFileName = GetCompressedFileName(); + WritingProcedure(packet, writer); - var destPath = Path.Combine(Path.GetDirectoryName(realFileName), version, - Path.GetFileName(realFileName)); + }, threadCount); - var destDir = Path.GetDirectoryName(destPath); - if (!Directory.Exists(destDir)) - Directory.CreateDirectory(destDir); + pwp.WaitForFinished(Timeout.Infinite); - File.Move(realFileName, destPath); + reader.PacketReader.Dispose(); - Trace.WriteLine("Moved " + realFileName + " to " + destPath); + _stats.SetEndTime(DateTime.Now); + } + if (written) + Trace.WriteLine($"{_logPrefix}: Saved file to '{outFileName}'"); + else + { + Trace.WriteLine($"{_logPrefix}: No file produced"); + File.Delete(outFileName); + } + + Trace.WriteLine($"{_logPrefix}: {_stats}"); + + if (Settings.SQLOutputFlag != 0 || HotfixSettings.Instance.ShouldLog()) + WriteSQLs(); + + if (Settings.LogPacketErrors) + WritePacketErrors(); + + GC.Collect(); // Force a GC collect after parsing a file. It seems to help. + } + + private static IWritingStrategy GetWriter(string outFileName) + { + IWritingStrategy writingStrategy = null; + + switch (Settings.OutputTextFormat) + { + case OutputFormat.Txt: + writingStrategy = new TextOutputWriter(outFileName); break; - } - case DumpFormatType.ConnectionIndexes: + case OutputFormat.Xml: + writingStrategy = new XmlOutputWriter(outFileName); + break; + default: + break; + } + return writingStrategy; + } + + private int SetupThreads(string outFileName) + { + Store.Store.SQLEnabledFlags = Settings.SQLOutputFlag; + File.Delete(outFileName); + + _stats.SetStartTime(DateTime.Now); + + var threadCount = Settings.Threads; + if (threadCount == 0) + threadCount = Environment.ProcessorCount; + + ThreadPool.SetMinThreads(threadCount + 2, 4); + + return threadCount; + } + + private Tuple ReadingProcedure(Reader reader) + { + if (!reader.PacketReader.CanRead()) + return Tuple.Create(null, true); + + Packet packet; + var b = reader.TryRead(out packet); + + return Tuple.Create(packet, b); + } + + private void ProcessingProcedure(Packet packet) + { + // Parse the packet, adding text to Writer and stuff to the stores + if (packet.Direction == Direction.BNClientToServer || + packet.Direction == Direction.BNServerToClient) + BattlenetHandler.ParseBattlenet(packet); + else + Handler.Parse(packet); + + // Update statistics + _stats.AddByStatus(packet.Status); + } + + private void WritingProcedure(Packet packet, IWritingStrategy writer) + { + // get packet header if necessary + if (Settings.LogPacketErrors) + { + switch (packet.Status) { - var packets = ReadPackets(); - if (packets.Count == 0) + case ParsedStatus.WithErrors: + _withErrorHeaders.Add(packet.GetHeader()); + break; + case ParsedStatus.NotParsed: + _skippedHeaders.Add(packet.GetHeader()); + break; + case ParsedStatus.NoStructure: + _noStructureHeaders.Add(packet.GetHeader()); break; + } + } - using (var writer = new StreamWriter(Path.ChangeExtension(FileName, null) + "_connidx.txt")) - { - if (ClientVersion.Build <= ClientVersionBuild.V6_0_3_19342) - writer.WriteLine("# Warning: versions before 6.1 might not have proper ConnectionIndex values."); + // ReSharper disable AccessToDisposedClosure + if (writer != null) + { + // Write to file + writer.Write(packet.Writer); + writer.Flush(); + //Console.Write("\r\n Continue..."); + //Console.ReadKey(); + //Console.Write("\r"); + } + // ReSharper restore AccessToDisposedClosure + // Close Writer, Stream - Dispose + packet.ClosePacket(); + } + + private void StatsAnalysis(List packets) + { - IEnumerable, Packet>> groupsOpcode = packets - .GroupBy(packet => Tuple.Create(packet.Opcode, packet.Direction)) - .OrderBy(grouping => grouping.Key.Item2); + var firstPacket = packets.First(); + var lastPacket = packets.Last(); + + // CSV format + // ReSharper disable once UseStringInterpolation + Trace.WriteLine(string.Format("{0};{1};{2};{3};{4};{5};{6};{7};{8}", + FileName, // - sniff file name + firstPacket.Time, // - time of first packet + lastPacket.Time, // - time of last packet + (lastPacket.Time - firstPacket.Time).TotalSeconds, // - sniff duration (seconds) + packets.Count, // - packet count + packets.AsParallel().Sum(packet => packet.Length), // - total packets size (bytes) + packets.AsParallel().Average(packet => packet.Length), // - average packet size (bytes) + packets.AsParallel().Min(packet => packet.Length), // - smaller packet size (bytes) + packets.AsParallel().Max(packet => packet.Length))); // - larger packet size (bytes) - foreach (var groupOpcode in groupsOpcode) - { - var groups = groupOpcode - .GroupBy(packet => packet.ConnectionIndex) - .OrderBy(grouping => grouping.Key) - .ToList(); + } - writer.Write("{0} {1,-50}: ", groupOpcode.Key.Item2, Opcodes.GetOpcodeName(groupOpcode.Key.Item1, groupOpcode.Key.Item2)); + private void ProcessIndexes(List packets) + { + using (var writer = new StreamWriter(Path.ChangeExtension(FileName, null) + "_connidx.txt")) + { + if (ClientVersion.Build <= ClientVersionBuild.V6_0_3_19342) + writer.WriteLine("# Warning: versions before 6.1 might not have proper ConnectionIndex values."); - for (var i = 0; i < groups.Count; i++) - { - var idx = groups[i].Key; - writer.Write("{0} ({1}{2})", idx, (idx & 1) != 0 ? "INSTANCE" : "REALM", (idx & 2) != 0 ? "_NEW" : ""); + IEnumerable, Packet>> groupsOpcode = packets + .GroupBy(packet => Tuple.Create(packet.Opcode, packet.Direction)) + .OrderBy(grouping => grouping.Key.Item2); - if (i != groups.Count - 1) - writer.Write(", "); - } + foreach (var groupOpcode in groupsOpcode) + { + var groups = groupOpcode + .GroupBy(packet => packet.ConnectionIndex) + .OrderBy(grouping => grouping.Key) + .ToList(); - writer.WriteLine(); - } + writer.Write("{0} {1,-50}: ", groupOpcode.Key.Item2, Opcodes.GetOpcodeName(groupOpcode.Key.Item1, groupOpcode.Key.Item2)); + + for (var i = 0; i < groups.Count; i++) + { + var idx = groups[i].Key; + writer.Write("{0} ({1}{2})", idx, (idx & 1) != 0 ? "INSTANCE" : "REALM", (idx & 2) != 0 ? "_NEW" : ""); + + if (i != groups.Count - 1) + writer.Write(", "); } - break; + writer.WriteLine(); } - case DumpFormatType.Fusion: - { - var packets = ReadPackets(); - if (packets.Count == 0) - break; + } + } - FusionDump(packets); - break; - } - default: + private void ProcessVersion() + { + var reader = _compression != FileCompression.None ? new Reader(_tempName, _sniffType) : new Reader(FileName, _sniffType); + + if (ClientVersion.IsUndefined() && reader.PacketReader.CanRead()) + { + Packet packet; + reader.TryRead(out packet); + packet.ClosePacket(); + } + + reader.PacketReader.Dispose(); + + var version = ClientVersion.IsUndefined() ? "unknown" : ClientVersion.VersionString; + + var realFileName = GetCompressedFileName(); + + var destPath = Path.Combine(Path.GetDirectoryName(realFileName), version, + Path.GetFileName(realFileName)); + + var destDir = Path.GetDirectoryName(destPath); + if (!Directory.Exists(destDir)) + Directory.CreateDirectory(destDir); + + File.Move(realFileName, destPath); + + Trace.WriteLine("Moved " + realFileName + " to " + destPath); + + } + + private void ProcessPacket(List packets) + { + if (Settings.FilterPacketsNum < 0) + { + var packetsPerSplit = Math.Abs(Settings.FilterPacketsNum); + var totalPackets = packets.Count; + + var numberOfSplits = (int)Math.Ceiling((double)totalPackets / packetsPerSplit); + + for (var i = 0; i < numberOfSplits; ++i) { - Trace.WriteLine($"{_logPrefix}: Dump format is none, nothing will be processed."); - break; + var fileNamePart = FileName + "_part_" + (i + 1) + ".pkt"; + + var packetsPart = packets.Take(packetsPerSplit).ToList(); + packets.RemoveRange(0, packetsPart.Count); + + BinaryDump(fileNamePart, packetsPart); } } + else + { + var fileNameExcerpt = Path.ChangeExtension(FileName, null) + "_excerpt.pkt"; + BinaryDump(fileNameExcerpt, packets); + } } public static string GetHeader(string fileName) @@ -438,7 +506,6 @@ public static string GetHeader(string fileName) "# Parsing date: " + DateTime.Now.ToString(CultureInfo.InvariantCulture) + Environment.NewLine; } - private static long _lastPercent; static void ShowPercentProgress(string message, long curr, long total) { var percent = (100 * curr) / total; @@ -606,5 +673,6 @@ private string GetCompressedFileName() { return FileName + _compression.GetExtension(); } + } } diff --git a/WowPacketParser/Loading/TextOutputWriter.cs b/WowPacketParser/Loading/TextOutputWriter.cs new file mode 100644 index 0000000000..115b2488d8 --- /dev/null +++ b/WowPacketParser/Loading/TextOutputWriter.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; + +namespace WowPacketParser.Loading +{ + public class TextOutputWriter : IWritingStrategy + { + private StreamWriter _writer; + + public TextOutputWriter(string outFileName) + { + _writer = new StreamWriter(outFileName, true); + } + + public void Write(object input) + { + _writer.WriteLine(input.ToString()); + } + + public void Flush() + { + _writer.Flush(); + } + + public void Dispose() + { + _writer.Dispose(); + } + } +} diff --git a/WowPacketParser/Loading/XmlOutputWriter.cs b/WowPacketParser/Loading/XmlOutputWriter.cs new file mode 100644 index 0000000000..d77cfb8c52 --- /dev/null +++ b/WowPacketParser/Loading/XmlOutputWriter.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using System.Xml; + +namespace WowPacketParser.Loading +{ + public class XmlOutputWriter : IWritingStrategy + { + private StreamWriter _writer; + private XmlDocument _document; + + public XmlOutputWriter(string outFileName) + { + _document = new XmlDocument(); + + if(!File.Exists(outFileName)) + { + XmlTextWriter textWritter = new XmlTextWriter(outFileName, null); + textWritter.WriteStartDocument(); + textWritter.WriteStartElement("packets"); + textWritter.WriteEndElement(); + + textWritter.Close(); + } + + _document.Load(outFileName); + + + //_writer = new StreamWriter(Console.OpenStandardOutput()); + _writer = new StreamWriter(outFileName, true); + } + + public void Write(object input) + { + XmlElement elem = _document.CreateElement("line"); + elem.InnerText = input.ToString(); + _document.GetElementsByTagName("packets")[0].AppendChild(elem); + } + + public void Flush() + { + _writer.Flush(); + } + + public void Dispose() + { + _document.Save(_writer); + _writer.Dispose(); + } + } +} diff --git a/WowPacketParser/Misc/IOutputBuilder.cs b/WowPacketParser/Misc/IOutputBuilder.cs new file mode 100644 index 0000000000..12f15e0fe8 --- /dev/null +++ b/WowPacketParser/Misc/IOutputBuilder.cs @@ -0,0 +1,9 @@ +namespace WowPacketParser.Misc +{ + public interface IOutputBuilder + { + void Append(string value); + + void Clear(); + } +} diff --git a/WowPacketParser/Misc/OutputFormat.cs b/WowPacketParser/Misc/OutputFormat.cs new file mode 100644 index 0000000000..65bf68b676 --- /dev/null +++ b/WowPacketParser/Misc/OutputFormat.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WowPacketParser.Misc +{ + public enum OutputFormat + { + Txt, + Xml + } +} diff --git a/WowPacketParser/Misc/Packet.cs b/WowPacketParser/Misc/Packet.cs index 06dcb8f955..e89a20f371 100644 --- a/WowPacketParser/Misc/Packet.cs +++ b/WowPacketParser/Misc/Packet.cs @@ -20,7 +20,7 @@ public sealed partial class Packet : BinaryReader private static DateTime _firstPacketTime; [SuppressMessage("Microsoft.Reliability", "CA2000", Justification = "MemoryStream is disposed in ClosePacket().")] - public Packet(byte[] input, int opcode, DateTime time, Direction direction, int number, StringBuilder writer, string fileName) + public Packet(byte[] input, int opcode, DateTime time, Direction direction, int number, IOutputBuilder writer, string fileName) : base(new MemoryStream(input, 0, input.Length), Encoding.UTF8) { Opcode = opcode; @@ -40,21 +40,8 @@ public Packet(byte[] input, int opcode, DateTime time, Direction direction, int [SuppressMessage("Microsoft.Reliability", "CA2000", Justification = "MemoryStream is disposed in ClosePacket().")] public Packet(byte[] input, int opcode, DateTime time, Direction direction, int number, string fileName) - : base(new MemoryStream(input, 0, input.Length), Encoding.UTF8) + : this(input, opcode, time, direction, number, null, fileName) { - Opcode = opcode; - Time = time; - Direction = direction; - Number = number; - Writer = null; - FileName = fileName; - Status = ParsedStatus.None; - WriteToFile = true; - - if (number == 0) - _firstPacketTime = Time; - - TimeSpan = Time - _firstPacketTime; } public int Opcode { get; set; } // setter can't be private because it's used in multiple_packets @@ -62,7 +49,7 @@ public Packet(byte[] input, int opcode, DateTime time, Direction direction, int public TimeSpan TimeSpan { get; } public Direction Direction { get; } public int Number { get; } - public StringBuilder Writer { get; private set; } + public IOutputBuilder Writer { get; private set; } public string FileName { get; } public ParsedStatus Status { get; set; } public bool WriteToFile { get; private set; } @@ -254,7 +241,7 @@ public void Write(string value) return; if (Writer == null) - Writer = new StringBuilder(); + Writer = new TextOutputBuilder(); Writer.Append(value); } @@ -265,9 +252,9 @@ public void Write(string format, params object[] args) return; if (Writer == null) - Writer = new StringBuilder(); + Writer = new TextOutputBuilder(); - Writer.AppendFormat(format, args); + Writer.Append(string.Format(format, args)); } public void WriteLine() @@ -276,9 +263,9 @@ public void WriteLine() return; if (Writer == null) - Writer = new StringBuilder(); + Writer = new TextOutputBuilder(); - Writer.AppendLine(); + Writer.Append(Environment.NewLine); } public void WriteLine(string value) @@ -287,9 +274,9 @@ public void WriteLine(string value) return; if (Writer == null) - Writer = new StringBuilder(); + Writer = new TextOutputBuilder(); - Writer.AppendLine(value); + Writer.Append(value + Environment.NewLine); } public void WriteLine(string format, params object[] args) @@ -298,9 +285,9 @@ public void WriteLine(string format, params object[] args) return; if (Writer == null) - Writer = new StringBuilder(); + Writer = new TextOutputBuilder(); - Writer.AppendLine(string.Format(format, args)); + Writer.Append(string.Format(format, args) + Environment.NewLine); } public void ClosePacket(bool clearWriter = true) diff --git a/WowPacketParser/Misc/Settings.cs b/WowPacketParser/Misc/Settings.cs index 439ec8535d..b9e731ff8f 100644 --- a/WowPacketParser/Misc/Settings.cs +++ b/WowPacketParser/Misc/Settings.cs @@ -29,6 +29,8 @@ public static class Settings public static readonly bool DevMode = Conf.GetBoolean("DevMode", false); public static readonly int Threads = Conf.GetInt("Threads", 8); + public static readonly OutputFormat OutputTextFormat = OutputFormat.Txt; + public static readonly bool SSHEnabled = Conf.GetBoolean("SSHEnabled", false); public static readonly string SSHHost = Conf.GetString("SSHHost", "localhost"); public static readonly string SSHUsername = Conf.GetString("SSHUsername", string.Empty); diff --git a/WowPacketParser/Misc/TextOutputBuilder.cs b/WowPacketParser/Misc/TextOutputBuilder.cs new file mode 100644 index 0000000000..217356fb41 --- /dev/null +++ b/WowPacketParser/Misc/TextOutputBuilder.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WowPacketParser.Misc +{ + class TextOutputBuilder : IOutputBuilder + { + private StringBuilder _builder; + + public TextOutputBuilder() + { + _builder = new StringBuilder(); + } + + public void Append(string value) + { + _builder.Append(value); + } + + public override string ToString() + { + return _builder.ToString(); + } + + public void Clear() + { + _builder.Clear(); + } + } +} diff --git a/WowPacketParser/WowPacketParser.csproj b/WowPacketParser/WowPacketParser.csproj index ac78f4218e..af03e4c32a 100644 --- a/WowPacketParser/WowPacketParser.csproj +++ b/WowPacketParser/WowPacketParser.csproj @@ -114,6 +114,7 @@ + False ..\packages\Tamir.SharpSSH.1.1.1.13\lib\Tamir.SharpSSH.dll @@ -400,7 +401,10 @@ + + + @@ -413,6 +417,9 @@ + + +