diff --git a/GlobalTerminalService/GTServer4.cs b/GlobalTerminalService/GTServer4.cs
index 000bf7f4..b0924bcd 100644
--- a/GlobalTerminalService/GTServer4.cs
+++ b/GlobalTerminalService/GTServer4.cs
@@ -6,6 +6,8 @@ using System.IO;
using PkmnFoundations.Support;
using PkmnFoundations.Structures;
using PkmnFoundations.Data;
+using System.Net.Sockets;
+using System.Diagnostics;
namespace PkmnFoundations.GlobalTerminalService
{
@@ -40,13 +42,16 @@ namespace PkmnFoundations.GlobalTerminalService
}
}
- protected override byte[] ProcessRequest(byte[] data)
+ protected override byte[] ProcessRequest(byte[] data, TcpClient c)
{
int length = BitConverter.ToInt32(data, 0);
AssertHelper.Equals(length, data.Length);
RequestTypes4 requestType = (RequestTypes4)data[4];
- Console.WriteLine("Handling Generation IV {0} request.", requestType);
+ StringBuilder logEntry = new StringBuilder();
+ logEntry.AppendFormat("Handling Generation IV {0} request.\nHost: {1}", requestType, c.Client.RemoteEndPoint);
+ logEntry.AppendLine();
+ EventLogEntryType type = EventLogEntryType.Information;
CryptMessage(data);
@@ -60,6 +65,8 @@ namespace PkmnFoundations.GlobalTerminalService
int pid = BitConverter.ToInt32(data, 8);
byte version = data[0x0c];
byte language = data[0x0d];
+ logEntry.AppendFormat("pid: {0}", pid);
+ logEntry.AppendLine();
switch (requestType)
{
@@ -67,6 +74,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x360)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -79,12 +88,13 @@ namespace PkmnFoundations.GlobalTerminalService
if (serial == 0)
{
- Console.WriteLine("Uploaded box already in server.");
+ logEntry.AppendLine("Uploaded box already in server.");
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
- Console.WriteLine("Box uploaded successfully.");
+ logEntry.AppendFormat("Box {0} uploaded successfully.", record.SerialNumber);
+ logEntry.AppendLine();
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
response.Write(BitConverter.GetBytes(serial), 0, 8);
@@ -93,6 +103,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x14c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -111,13 +123,16 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(result.SerialNumber), 0, 8);
response.Write(result.Data, 0, 0x21c);
}
- Console.WriteLine("Retrieved {0} boxes.", results.Length);
+ logEntry.AppendFormat("Retrieved {0} boxes.", results.Length);
+ logEntry.AppendLine();
} break;
case RequestTypes4.DressupUpload:
{
if (data.Length != 0x220)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -129,12 +144,12 @@ namespace PkmnFoundations.GlobalTerminalService
if (serial == 0)
{
- Console.WriteLine("Uploaded dressup already in server.");
+ logEntry.AppendLine("Uploaded dressup already in server.");
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
- Console.WriteLine("Dressup uploaded successfully.");
+ logEntry.AppendFormat("Dressup {0} uploaded successfully.", record.SerialNumber);
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
response.Write(BitConverter.GetBytes(serial), 0, 8);
@@ -143,6 +158,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x14c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -160,13 +177,16 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(result.SerialNumber), 0, 8);
response.Write(result.Data, 0, 0xe0);
}
- Console.WriteLine("Retrieved {0} dressup results.", results.Length);
+ logEntry.AppendFormat("Retrieved {0} dressup results.", results.Length);
+ logEntry.AppendLine();
} break;
case RequestTypes4.BattleVideoUpload:
{
if (data.Length != 0x1e8c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -178,12 +198,13 @@ namespace PkmnFoundations.GlobalTerminalService
if (serial == 0)
{
- Console.WriteLine("Uploaded battle video already in server.");
+ logEntry.AppendFormat("Uploaded battle video already in server.");
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
- Console.WriteLine("Battle video uploaded successfully.");
+ logEntry.AppendFormat("Battle video {0} uploaded successfully.", BattleVideoHeader4.FormatSerial(record.SerialNumber));
+ logEntry.AppendLine();
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
response.Write(BitConverter.GetBytes(serial), 0, 8);
@@ -192,6 +213,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x15c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -202,15 +225,15 @@ namespace PkmnFoundations.GlobalTerminalService
byte country = data[0x147];
byte region = data[0x148];
- Console.Write("Searching for ");
+ logEntry.AppendFormat("Searching for ");
if (species != 0xffff)
- Console.Write("species {0}, ", species);
- Console.Write("{0}", meta);
+ logEntry.AppendFormat("species {0}, ", species);
+ logEntry.AppendFormat("{0}", meta);
if (country != 0xff)
- Console.Write(", country {0}", country);
+ logEntry.AppendFormat(", country {0}", country);
if (region != 0xff)
- Console.Write(", region {0}", region);
- Console.WriteLine(".");
+ logEntry.AppendFormat(", region {0}", region);
+ logEntry.AppendLine(".");
BattleVideoHeader4[] results = DataAbstract.Instance.BattleVideoSearch4(species, meta, country, region, 30);
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
@@ -222,13 +245,16 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(result.SerialNumber), 0, 8);
response.Write(result.Data, 0, 0xe4);
}
- Console.WriteLine("Retrieved {0} battle video results.", results.Length);
+ logEntry.AppendFormat("Retrieved {0} battle video results.", results.Length);
+ logEntry.AppendLine();
} break;
case RequestTypes4.BattleVideoWatch:
{
if (data.Length != 0x14c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -238,7 +264,9 @@ namespace PkmnFoundations.GlobalTerminalService
if (record == null)
{
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
- Console.WriteLine("Requested battle video {0} was missing.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendFormat("Requested battle video {0} was missing.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendLine();
+ type = EventLogEntryType.FailureAudit;
break;
}
@@ -247,17 +275,20 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(record.SerialNumber), 0, 8);
response.Write(record.Header.Data, 0, 0xe4);
response.Write(record.Data, 0, 0x1c68);
- Console.WriteLine("Retrieved battle video {0}.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendFormat("Retrieved battle video {0}.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendLine();
} break;
default:
+ logEntry.AppendLine("Unrecognized request type.");
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
}
catch (Exception ex)
{
- Console.WriteLine(ex.ToString());
+ logEntry.AppendFormat("Unhandled exception while handling request.\nException: {0}", ex.ToString());
+ logEntry.AppendLine();
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
}
@@ -265,6 +296,8 @@ namespace PkmnFoundations.GlobalTerminalService
byte[] responseData = response.ToArray();
WriteLength(responseData);
CryptMessage(responseData);
+
+ LogHelper.Write(logEntry.ToString(), type);
return responseData;
}
diff --git a/GlobalTerminalService/GTServer5.cs b/GlobalTerminalService/GTServer5.cs
index c89892c9..33a5e02f 100644
--- a/GlobalTerminalService/GTServer5.cs
+++ b/GlobalTerminalService/GTServer5.cs
@@ -7,6 +7,8 @@ using System.IO;
using PkmnFoundations.Structures;
using PkmnFoundations.Data;
using System.Security.Cryptography.X509Certificates;
+using System.Net.Sockets;
+using System.Diagnostics;
namespace PkmnFoundations.GlobalTerminalService
{
@@ -41,13 +43,16 @@ namespace PkmnFoundations.GlobalTerminalService
}
- protected override byte[] ProcessRequest(byte[] data)
+ protected override byte[] ProcessRequest(byte[] data, TcpClient c)
{
int length = BitConverter.ToInt32(data, 0);
AssertHelper.Equals(length, data.Length);
RequestTypes5 requestType = (RequestTypes5)data[4];
- Console.WriteLine("Handling Generation V {0} request.", requestType);
+ StringBuilder logEntry = new StringBuilder();
+ logEntry.AppendFormat("Handling Generation V {0} request.\nHost: {1}", requestType, c.Client.RemoteEndPoint);
+ logEntry.AppendLine();
+ EventLogEntryType type = EventLogEntryType.Information;
MemoryStream response = new MemoryStream();
response.Write(new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0, 4); // placeholder for length
@@ -59,6 +64,8 @@ namespace PkmnFoundations.GlobalTerminalService
int pid = BitConverter.ToInt32(data, 8);
byte version = data[0x0c];
byte language = data[0x0d];
+ logEntry.AppendFormat("pid: {0}", pid);
+ logEntry.AppendLine();
switch (requestType)
{
@@ -66,6 +73,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x370)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -77,12 +86,13 @@ namespace PkmnFoundations.GlobalTerminalService
if (serial == 0)
{
- Console.WriteLine("Uploaded musical already in server.");
+ logEntry.AppendLine("Uploaded musical already in server.");
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
- Console.WriteLine("Musical uploaded successfully.");
+ logEntry.AppendFormat("Musical {0} uploaded successfully.", record.SerialNumber);
+ logEntry.AppendLine();
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
response.Write(BitConverter.GetBytes(serial), 0, 8);
@@ -91,6 +101,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x14c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -108,7 +120,8 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(result.SerialNumber), 0, 8);
response.Write(result.Data, 0, 0x230);
}
- Console.WriteLine("Retrieved {0} dressup results.", results.Length);
+ logEntry.AppendFormat("Retrieved {0} dressup results.", results.Length);
+ logEntry.AppendLine();
} break;
@@ -116,6 +129,8 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x1ae8)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -138,12 +153,13 @@ namespace PkmnFoundations.GlobalTerminalService
if (serial == 0)
{
- Console.WriteLine("Uploaded battle video already in server.");
+ logEntry.AppendLine("Uploaded battle video already in server.");
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
- Console.WriteLine("Battle video uploaded successfully.");
+ logEntry.AppendFormat("Battle video {0} uploaded successfully.", BattleVideoHeader4.FormatSerial(record.SerialNumber));
+ logEntry.AppendLine();
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
response.Write(BitConverter.GetBytes(serial), 0, 8);
@@ -152,12 +168,14 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (data.Length != 0x15c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
// todo: validate or log some of this?
- BattleVideoRankings5 type = (BattleVideoRankings5)BitConverter.ToUInt32(data, 0x140);
+ BattleVideoRankings5 ranking = (BattleVideoRankings5)BitConverter.ToUInt32(data, 0x140);
ushort species = BitConverter.ToUInt16(data, 0x144);
BattleVideoMetagames5 meta = (BattleVideoMetagames5)data[0x146];
@@ -169,19 +187,19 @@ namespace PkmnFoundations.GlobalTerminalService
byte country = data[0x14a];
byte region = data[0x14b];
- Console.Write("Searching for ");
- if (type != BattleVideoRankings5.None)
- Console.Write("{0}, ", type);
+ logEntry.Append("Searching for ");
+ if (ranking != BattleVideoRankings5.None)
+ logEntry.AppendFormat("{0}, ", type);
if (species != 0xffff)
- Console.Write("species {0}, ", species);
- Console.Write("{0}", meta);
+ logEntry.AppendFormat("species {0}, ", species);
+ logEntry.AppendFormat("{0}", meta);
if (country != 0xff)
- Console.Write(", country {0}", country);
+ logEntry.AppendFormat(", country {0}", country);
if (region != 0xff)
- Console.Write(", region {0}", region);
- Console.WriteLine(".");
+ logEntry.AppendFormat(", region {0}", region);
+ logEntry.AppendLine(".");
- BattleVideoHeader5[] results = DataAbstract.Instance.BattleVideoSearch5(species, type, meta, country, region, 30);
+ BattleVideoHeader5[] results = DataAbstract.Instance.BattleVideoSearch5(species, ranking, meta, country, region, 30);
response.Write(new byte[] { 0x00, 0x00 }, 0, 2); // result code (0 for OK)
response.Write(BitConverter.GetBytes(results.Length), 0, 4);
@@ -191,13 +209,16 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(result.SerialNumber), 0, 8);
response.Write(result.Data, 0, 0xc4);
}
- Console.WriteLine("Retrieved {0} battle video results.", results.Length);
+ logEntry.AppendFormat("Retrieved {0} battle video results.", results.Length);
+ logEntry.AppendLine();
} break;
case RequestTypes5.BattleVideoWatch:
{
if (data.Length != 0x14c)
{
+ logEntry.AppendLine("Length did not validate.");
+ type = EventLogEntryType.FailureAudit;
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
@@ -207,7 +228,9 @@ namespace PkmnFoundations.GlobalTerminalService
if (record == null)
{
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
- Console.WriteLine("Requested battle video {0} was missing.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendFormat("Requested battle video {0} was missing.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendLine();
+ type = EventLogEntryType.FailureAudit;
break;
}
@@ -216,24 +239,30 @@ namespace PkmnFoundations.GlobalTerminalService
response.Write(BitConverter.GetBytes(record.SerialNumber), 0, 8);
response.Write(record.Header.Data, 0, 0xc4);
response.Write(record.Data, 0, 0x17e0);
- Console.WriteLine("Retrieved battle video {0}.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendFormat("Retrieved battle video {0}.", BattleVideoHeader4.FormatSerial(serial));
+ logEntry.AppendLine();
} break;
default:
+ logEntry.AppendLine("Unrecognized request type.");
+ response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
}
catch (Exception ex)
{
- Console.WriteLine(ex.ToString());
+ logEntry.AppendFormat("Unhandled exception while handling request.\nException: {0}", ex.ToString());
+ logEntry.AppendLine();
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
}
response.Flush();
byte[] responseData = response.ToArray();
WriteLength(responseData);
+
+ LogHelper.Write(logEntry.ToString(), type);
return responseData;
}
diff --git a/GlobalTerminalService/GTServerBase.cs b/GlobalTerminalService/GTServerBase.cs
index 41ee0a1d..e1e1bfed 100644
--- a/GlobalTerminalService/GTServerBase.cs
+++ b/GlobalTerminalService/GTServerBase.cs
@@ -9,6 +9,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;
using PkmnFoundations.Support;
+using System.Diagnostics;
namespace PkmnFoundations.GlobalTerminalService
{
@@ -79,7 +80,7 @@ namespace PkmnFoundations.GlobalTerminalService
{
if (m_workers.Count > 0) return;
- Console.WriteLine("{0} server running on port {1} with {2} threads.", Title, ((IPEndPoint)m_listener.LocalEndpoint).Port, Threads);
+ LogHelper.Write(String.Format("{0} server running on port {1} with {2} threads.", Title, ((IPEndPoint)m_listener.LocalEndpoint).Port, Threads));
m_closing = false;
m_listener.Start();
@@ -108,7 +109,8 @@ namespace PkmnFoundations.GlobalTerminalService
private void MainLoop(object o)
{
int threadIndex = m_workers.IndexOf(Thread.CurrentThread);
- Console.WriteLine("Thread {0} begins.", threadIndex);
+ // This is too chatty for an event log.
+ //LogHelper.Write(String.Format("Thread {0} begins.", threadIndex));
while (!m_closing)
{
@@ -127,28 +129,52 @@ namespace PkmnFoundations.GlobalTerminalService
continue;
}
- c.ReceiveTimeout = Timeout;
- c.SendTimeout = Timeout;
+ try
+ {
+ c.ReceiveTimeout = Timeout;
+ c.SendTimeout = Timeout;
- Stream s = GetStream(c);
- BinaryReader br = new BinaryReader(s);
+ Stream s = GetStream(c);
+ BinaryReader br = new BinaryReader(s);
- int length = br.ReadInt32();
- byte[] data = new byte[length];
- BitConverter.GetBytes(length).CopyTo(data, 0);
- br.ReadBlock(data, 4, length - 4);
+ int length = br.ReadInt32();
+ if (length > 7820)
+ {
+ LogHelper.Write(String.Format("Indicated request length is over limit.\nHost: {0}", c.Client.RemoteEndPoint), EventLogEntryType.FailureAudit);
+ continue;
+ }
+ if (length < 320)
+ {
+ LogHelper.Write(String.Format("Indicated request length is under limit.\nHost: {0}", c.Client.RemoteEndPoint), EventLogEntryType.FailureAudit);
+ continue;
+ }
- byte[] response = ProcessRequest(data);
- s.Write(response, 0, response.Length);
+ byte[] data = new byte[length];
+ BitConverter.GetBytes(length).CopyTo(data, 0);
+
+ int actualLength = br.ReadBlock(data, 4, length - 4);
+ if (actualLength + 4 != length)
+ {
+ LogHelper.Write(String.Format("The client disconnected prematurely.\nHost: {0}", c.Client.RemoteEndPoint), EventLogEntryType.FailureAudit);
+ continue;
+ }
+
+ byte[] response = ProcessRequest(data, c);
+ s.Write(response, 0, response.Length);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.Write(String.Format("Unhandled exception while handling request:\nHost: {0}\nException: {1}", c.Client.RemoteEndPoint, ex.Message), EventLogEntryType.Error);
+ }
}
}
catch (Exception ex)
{
- Console.WriteLine(ex.ToString());
+ LogHelper.Write(String.Format("Unhandled exception while handling request:\nException: {0}", ex.Message), EventLogEntryType.Error);
}
}
- Console.WriteLine("Thread {0} ends.", threadIndex);
+ //LogHelper.Write(String.Format("Thread {0} ends.", threadIndex));
m_workers.Remove(Thread.CurrentThread);
}
@@ -172,7 +198,7 @@ namespace PkmnFoundations.GlobalTerminalService
else return c.GetStream();
}
- protected abstract byte[] ProcessRequest(byte[] data);
+ protected abstract byte[] ProcessRequest(byte[] data, TcpClient c);
public abstract String Title { get; }
diff --git a/library/Library.csproj b/library/Library.csproj
index 055bdf66..60a6bab6 100644
--- a/library/Library.csproj
+++ b/library/Library.csproj
@@ -66,6 +66,7 @@
+
diff --git a/library/Support/AssertHelper.cs b/library/Support/AssertHelper.cs
index 2ff22e6a..374a6b37 100644
--- a/library/Support/AssertHelper.cs
+++ b/library/Support/AssertHelper.cs
@@ -10,25 +10,11 @@ namespace PkmnFoundations.Support
{
public static void Assert(bool condition, String message)
{
- if (!condition) try
- {
- using (StreamWriter s = File.CreateText("assert.log"))
- {
- s.Write("Assert failed: ");
- s.WriteLine(message);
- s.Write("Date: ");
- s.WriteLine(DateTime.Now.ToString("G"));
- s.Write("Stack trace: ");
- s.WriteLine(new StackTrace(true).ToString());
- s.WriteLine();
- }
- }
- catch (Exception)
- {
- // directory not found or no write permissions there
- }
+ if (!condition) LogHelper.Write(message, EventLogEntryType.Error);
+#if DEBUG
Debug.Assert(condition, message);
+#endif
}
public static void Assert(bool condition)
@@ -38,12 +24,12 @@ namespace PkmnFoundations.Support
public static void Unreachable()
{
- Assert(false, "Unreachable code has been reached.");
+ Assert(false, "Assert failed: Unreachable code has been reached.");
}
public static void Equals(T first, T second) where T : IEquatable
{
- Assert(((IEquatable)first).Equals((IEquatable)second), "Values are not equal.");
+ Assert(((IEquatable)first).Equals((IEquatable)second), "Assert failed: Values are not equal.");
}
}
}
diff --git a/library/Support/LogHelper.cs b/library/Support/LogHelper.cs
new file mode 100644
index 00000000..09c3439d
--- /dev/null
+++ b/library/Support/LogHelper.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace PkmnFoundations.Support
+{
+ public static class LogHelper
+ {
+ static LogHelper()
+ {
+ Type = EventLogTypes.StandardError;
+ }
+
+ public static void Write(String message, EventLogEntryType type, int eventID, ushort category, byte[] rawData)
+ {
+ switch (Type)
+ {
+ case EventLogTypes.StandardOutput:
+ Console.WriteLine(message);
+ break;
+ case EventLogTypes.StandardError:
+ Console.Error.WriteLine(message);
+ break;
+ case EventLogTypes.File:
+ try
+ {
+ using (FileStream fs = File.Open(m_filename, FileMode.Append))
+ {
+ StreamWriter sw = new StreamWriter(fs);
+ sw.Write("{0}\t{1}\n", DateTime.Now, message);
+ sw.Close();
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Can't open logfile at {0}.\nException: {1}\nMessage: {2}", m_filename, ex.Message, message);
+ }
+ break;
+ case EventLogTypes.Windows:
+ m_event_log.WriteEntry(message, type, eventID, (short)category, rawData);
+ break;
+ }
+ }
+
+ public static void Write(String message, EventLogEntryType type)
+ {
+ Write(message, type, 0, 0, null);
+ }
+
+ public static void Write(String message)
+ {
+ Write(message, EventLogEntryType.Information, 0, 0, null);
+ }
+
+ public static void UseStandardOutput()
+ {
+ Type = EventLogTypes.StandardOutput;
+ m_event_log = null;
+ }
+
+ public static void UseStandardError()
+ {
+ Type = EventLogTypes.StandardError;
+ m_event_log = null;
+ }
+
+ public static void UseFile(String filename)
+ {
+ if (filename == null) throw new ArgumentNullException("filename");
+ Type = EventLogTypes.File;
+ m_filename = filename;
+ m_event_log = null;
+ }
+
+ public static void UseEventLog(EventLog event_log)
+ {
+ if (event_log == null) throw new ArgumentNullException("event_log");
+ Type = EventLogTypes.Windows;
+ m_event_log = event_log;
+ }
+
+ public static EventLogTypes Type
+ {
+ get;
+ private set;
+ }
+
+ private static String m_filename;
+ private static EventLog m_event_log;
+ }
+
+ public enum EventLogTypes
+ {
+ StandardOutput,
+ StandardError,
+ File,
+ Windows
+ }
+}