diff --git a/gts/pkvldtprod.ashx.cs b/gts/pkvldtprod.ashx.cs index 38855535..f8bdf656 100644 --- a/gts/pkvldtprod.ashx.cs +++ b/gts/pkvldtprod.ashx.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using System.IO; +using PkmnFoundations.Support; +using System.Text; namespace PkmnFoundations.GTS { @@ -13,28 +16,81 @@ namespace PkmnFoundations.GTS public void ProcessRequest(HttpContext context) { - // todo... + byte[] requestData = new byte[(int)context.Request.InputStream.Length]; + context.Request.InputStream.ReadBlock(requestData, 0, (int)context.Request.InputStream.Length); + + // this is a mysterious token of unknown purpose. It seems to vary with the + // type of request being done. + // On GTS requests, it's 83 characters long and begins with NDS. + // In Random Matchup, it looks more like a base64 string, is 88 chars long + // and encodes 64 bytes of random looking data. + int tokenLength = Array.IndexOf(requestData, 0x00); + String token = StringHelper.BytesToString(requestData, 0, tokenLength, Encoding.UTF8); + int offset = tokenLength + 1; + + RequestType type = (RequestType)BitConverter.ToInt16(requestData, offset); + offset += 2; + + int pkmCount; + switch (type) + { + case RequestType.RandomMatchup: + case RequestType.GTS: + { + pkmCount = (requestData.Length - offset) / 220; + } break; + case RequestType.BattleSubway: + { + pkmCount = 6; + // todo: Need more info on this structure + } break; + default: + { + // Don't understand this request. Give it a response containing + // all 00s so stuff depending on it won't break. + // The game accepts a hash of all 00s and doesn't care what's contained + // in the response beyond its expected length so this will work. + // fixme: Once we start generating real signatures, they will prevent + // this from returning the necessary number of 00s in all cases. + pkmCount = 6; + } break; + } + context.Response.ContentType = "text/plain"; - context.Response.OutputStream.Write(new byte[]{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0, 149); + context.Response.OutputStream.WriteByte((byte)PartyValidationResult.Valid); // success + for (int x = 0; x < pkmCount; x++) + { + context.Response.OutputStream.Write(BitConverter.GetBytes((int)PokemonValidationResult.Valid), 0, 4); + } + // placeholder for signature. + // Should be 128 bytes of an unknown hashing/signing algorithm + context.Response.OutputStream.Write(new byte[128], 0, 128); + } + + private enum PartyValidationResult : byte + { + Valid = 0x00, + Invalid = 0x01 + } + + private enum PokemonValidationResult : int + { + Valid = 0x00000000, + Invalid = 0x3c000000 + } + + private enum RequestType : short + { + RandomMatchup = 0x0000, + GTS = 0x0100, + BattleSubway = 0x0400 + } + + private void Error400(HttpContext context) + { + context.Response.StatusCode = 400; + context.Response.ContentType = "text/plain"; + context.Response.Write("Bad request"); } public bool IsReusable diff --git a/library/Library.csproj b/library/Library.csproj index 1038360c..d7076f63 100644 --- a/library/Library.csproj +++ b/library/Library.csproj @@ -63,6 +63,8 @@ + + diff --git a/library/Support/StreamExtender.cs b/library/Support/StreamExtender.cs new file mode 100644 index 00000000..6f31f718 --- /dev/null +++ b/library/Support/StreamExtender.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace PkmnFoundations.Support +{ + public static class StreamExtender + { + /// + /// Reads bytes from a stream and waits until it can read them all. + /// + /// + /// + /// + /// + public static int ReadBlock(this Stream s, byte[] buffer, int offset, int count) + { + int readBytes = 0; + while (readBytes < count) + { + int x = s.Read(buffer, offset + readBytes, count - readBytes); + if (x == 0) return readBytes; + readBytes += x; + } + return readBytes; + } + + } +} diff --git a/library/Support/StringHelper.cs b/library/Support/StringHelper.cs new file mode 100644 index 00000000..3c088b89 --- /dev/null +++ b/library/Support/StringHelper.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace PkmnFoundations.Support +{ + public static class StringHelper + { + public static String BytesToString(byte[] data, Encoding encoding) + { + MemoryStream ms = new MemoryStream(data); + StreamReader sr = new StreamReader(ms, encoding); + return sr.ReadToEnd(); + } + + public static String BytesToString(byte[] data, int start, int length, Encoding encoding) + { + MemoryStream ms = new MemoryStream(data, start, length); + StreamReader sr = new StreamReader(ms, encoding); + return sr.ReadToEnd(); + } + } +}