-using System;\r
+// Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
+//\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
+//\r
+// http://www.apache.org/licenses/LICENSE-2.0\r
+//\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
+\r
+using System;\r
+using System.Collections.Generic;\r
+using System.Globalization;\r
using System.IO;\r
using System.Linq;\r
-using System.Net;\r
using System.Net.Sockets;\r
using System.Text;\r
-using System.Threading;\r
-using System.Web;\r
-using System.Windows.Forms;\r
+using System.Text.RegularExpressions;\r
\r
namespace KancolleSniffer\r
{\r
public class LogServer\r
{\r
- private readonly TcpListener _listener;\r
- private readonly Thread _thread;\r
- private readonly string _indexDir = Path.GetDirectoryName(Application.ExecutablePath);\r
- private string _outputDir = Path.GetDirectoryName(Application.ExecutablePath);\r
+ private static readonly string IndexDir = AppDomain.CurrentDomain.BaseDirectory;\r
+ private static string _outputDir = AppDomain.CurrentDomain.BaseDirectory;\r
\r
- public string OutputDir\r
+ public static string OutputDir\r
{\r
- set { _outputDir = value; }\r
+ set => _outputDir = value;\r
}\r
\r
- public LogServer(int port)\r
+ public static MaterialCount[] MaterialHistory { private get; set; }\r
+\r
+ public static void Process(Socket client, string requestLine)\r
{\r
- _listener = new TcpListener(IPAddress.Loopback, port);\r
- _thread = new Thread(Listen);\r
+ var from = DateTime.MinValue;\r
+ var to = DateTime.MaxValue;\r
+ var timestamp = false;\r
+\r
+ var request = requestLine.Split(' ');\r
+ if (request.Length != 3)\r
+ {\r
+ SendError(client, "400 Bad Request");\r
+ return;\r
+ }\r
+ if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase))\r
+ {\r
+ SendError(client, "501 Not Implemented");\r
+ return;\r
+ }\r
+ var tmp = request[1].Split('?');\r
+ var path = HttpUtility.UrlDecode(tmp[0]);\r
+ if (path == null || !path.StartsWith("/"))\r
+ {\r
+ SendError(client, "400 Bad Request");\r
+ return;\r
+ }\r
+ if (tmp.Length == 2)\r
+ {\r
+ var query = HttpUtility.ParseQueryString(tmp[1]);\r
+ if (query["from"] != null)\r
+ {\r
+ double.TryParse(query["from"], out var tick);\r
+ from = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
+ }\r
+ if (query["to"] != null)\r
+ {\r
+ double.TryParse(query["to"], out var tick);\r
+ to = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
+ }\r
+ if (query["number"] != null)\r
+ timestamp = query["number"] == "true";\r
+ }\r
+\r
+ path = path == "/" ? "index.html" : path.Substring(1);\r
+ var full = Path.Combine(IndexDir, path);\r
+ var csv = Path.Combine(_outputDir, path);\r
+ if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
+ {\r
+ SendFile(client, full, "text/html");\r
+ return;\r
+ }\r
+ if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv))\r
+ {\r
+ SendFile(client, csv, "text/csv; charset=Shift_JIS");\r
+ return;\r
+ }\r
+ if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))\r
+ {\r
+ SendJsonData(client, csv, from, to, timestamp);\r
+ return;\r
+ }\r
+ if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
+ {\r
+ SendFile(client, full, "application/javascript");\r
+ return;\r
+ }\r
+ if (path.EndsWith("proxy.pac"))\r
+ {\r
+ SendProxyPac(client, HttpProxy.LocalPort);\r
+ return;\r
+ }\r
+ SendError(client, "404 Not Found");\r
}\r
\r
- public void Start()\r
+ private static void SendError(Socket client, string error)\r
{\r
- _thread.Start();\r
+ using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
+ {\r
+ writer.Write("HTTP/1.1 {0}\r\n", error);\r
+ writer.Write("Server: KancolleSniffer\r\n");\r
+ writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
+ writer.Write("Connection: close\r\n\r\n");\r
+ writer.Write("<html><head><title>{0}</title></head>\r\n", error);\r
+ writer.Write("<body><h4>{0}</h4></body></html>\r\n\r\n", error);\r
+ writer.Flush();\r
+ client.Send(((MemoryStream)writer.BaseStream).ToArray());\r
+ }\r
}\r
\r
- private void Listen()\r
+ private static void SendJsonData(Socket client, string path, DateTime from, DateTime to, bool number)\r
{\r
+ using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
+ {\r
+ header.Write("HTTP/1.1 200 OK\r\n");\r
+ header.Write("Server: KancolleSniffer\r\n");\r
+ header.Write("Date: {0:R}\r\n", DateTime.Now);\r
+ header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS");\r
+ header.Write("Connection: close\r\n\r\n");\r
+ header.Flush();\r
+ client.Send(((MemoryStream)header.BaseStream).ToArray());\r
+ }\r
+ var csv = path.Replace(".json", ".csv");\r
+ var encoding = Encoding.GetEncoding("Shift_JIS");\r
+ client.Send(encoding.GetBytes("{ \"data\": [\n"));\r
+ var battle = false;\r
+ var material = false;\r
try\r
{\r
- _listener.Start();\r
- while (true)\r
+ if (!File.Exists(csv))\r
+ return;\r
+ var records = 0;\r
+ if (path.EndsWith("遠征報告書.json"))\r
+ {\r
+ records = 10;\r
+ }\r
+ else if (path.EndsWith("改修報告書.json"))\r
+ {\r
+ records = 15;\r
+ }\r
+ else if (path.EndsWith("海戦・ドロップ報告書.json"))\r
{\r
- var data = new byte[4096];\r
- var client = _listener.AcceptSocket();\r
- try\r
+ records = 39;\r
+ battle = true;\r
+ }\r
+ else if (path.EndsWith("開発報告書.json"))\r
+ {\r
+ records = 9;\r
+ }\r
+ else if (path.EndsWith("建造報告書.json"))\r
+ {\r
+ records = 12;\r
+ }\r
+ else if (path.EndsWith("資材ログ.json"))\r
+ {\r
+ records = 9;\r
+ material = true;\r
+ }\r
+ else if (path.EndsWith("戦果.json"))\r
+ {\r
+ records = 3;\r
+ }\r
+ var delimiter = "";\r
+ foreach (var line in File.ReadLines(csv, encoding).Skip(1))\r
+ {\r
+ var data = line.Split(',');\r
+ if (!DateTime.TryParseExact(data[0], Logger.DateTimeFormat, CultureInfo.InvariantCulture,\r
+ DateTimeStyles.AssumeLocal, out DateTime date))\r
{\r
- if (client.Available == 0)\r
+ if (DateTime.TryParse(data[0], CultureInfo.CurrentCulture,\r
+ DateTimeStyles.AssumeLocal, out date))\r
{\r
- Thread.Sleep(500);\r
- if (client.Available == 0)\r
- continue;\r
+ data[0] = date.ToString(Logger.DateTimeFormat);\r
}\r
- if (client.Receive(data) == 0)\r
- continue;\r
- var request = Encoding.UTF8.GetString(data).Split('\r')[0].Split(' ');\r
- if (request.Length != 3)\r
+ else\r
{\r
- SendError(client, "400 Bad Request");\r
continue;\r
}\r
- if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase))\r
- {\r
- SendError(client, "501 Not Implemented");\r
- continue;\r
- }\r
- var path = HttpUtility.UrlDecode(request[1].Split('?')[0]);\r
- if (path == null || !path.StartsWith("/"))\r
- {\r
- SendError(client, "400 Bad Request");\r
- continue;\r
- }\r
-\r
- path = path == "/" ? "index.html" : path.Substring(1);\r
- var full = Path.Combine(_indexDir, path);\r
- var csv = Path.Combine(_outputDir, path);\r
- if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
- {\r
- SendFile(client, full, "text/html");\r
- continue;\r
- }\r
- if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv))\r
- {\r
- SendFile(client, csv, "text/csv; charset=Shift_JIS");\r
- continue;\r
- }\r
- if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase) &&\r
- File.Exists(csv.Replace(".json", ".csv")))\r
- {\r
- SendJsonData(client, csv);\r
- continue;\r
- }\r
- if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
- {\r
- SendFile(client, full, "application/javascript");\r
- continue;\r
- }\r
- SendError(client, "404 Not Found");\r
}\r
- catch (IOException)\r
+ if (date < from || to < date)\r
+ continue;\r
+ IEnumerable<string> entries = data;\r
+ if (material)\r
+ entries = data.Take(9);\r
+ if (battle)\r
+ entries = BattleLogProcessor.Process(data);\r
+ if (entries.Count() != records)\r
+ continue;\r
+ if (number)\r
{\r
+ var stamp = ((date.ToUniversalTime().Ticks -\r
+ new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks) /\r
+ TimeSpan.TicksPerMillisecond).ToString();\r
+ client.Send(encoding.GetBytes(delimiter + "[" + stamp + "," +\r
+ string.Join(",", entries.Skip(1)) + "]"));\r
}\r
- catch (InvalidOperationException)\r
+ else\r
{\r
+ client.Send(encoding.GetBytes(delimiter + "[\"" +\r
+ string.Join("\",\"", entries) + "\"]"));\r
}\r
- finally\r
- {\r
- client.Close();\r
- }\r
+ delimiter = ",\n";\r
+ }\r
+ if (material && !number)\r
+ {\r
+ client.Send(encoding.GetBytes(delimiter + "[\"" +\r
+ string.Join("\",\"", GetCurrentMaterialRecord()) + "\"]"));\r
}\r
}\r
- catch (SocketException)\r
+ finally\r
{\r
+ client.Send(encoding.GetBytes("]}\n"));\r
}\r
- finally\r
+ }\r
+\r
+ private static IEnumerable<string> GetCurrentMaterialRecord()\r
+ {\r
+ return new[] {DateTime.Now.ToString(Logger.DateTimeFormat)}.\r
+ Concat(MaterialHistory.Select(c => c.Now.ToString()));\r
+ }\r
+\r
+ private static void SendFile(Socket client, string path, string mime)\r
+ {\r
+ using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
{\r
- _listener.Stop();\r
+ header.Write("HTTP/1.1 200 OK\r\n");\r
+ header.Write("Server: KancolleSniffer\r\n");\r
+ header.Write("Date: {0:R}\r\n", DateTime.Now);\r
+ header.Write("Content-Length: {0}\r\n", new FileInfo(path).Length);\r
+ header.Write("Content-Type: {0}\r\n", mime);\r
+ header.Write("Connection: close\r\n\r\n");\r
+ header.Flush();\r
+ client.SendFile(path, ((MemoryStream)header.BaseStream).ToArray(), null,\r
+ TransmitFileOptions.UseDefaultWorkerThread);\r
}\r
}\r
\r
- private void SendError(Socket client, string error)\r
+ private static void SendProxyPac(Socket client, int port)\r
{\r
- using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
+ using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
{\r
- writer.Write("HTTP/1.1 {0}\r\n", error);\r
- writer.Write("Server: KancolleSniffer\r\n");\r
- writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
- writer.Write("Connection: close\r\n\r\n");\r
- writer.Write("<html><head><title>{0}</title></head>\r\n", error);\r
- writer.Write("<body><h4>{0}</h4></body></html>\r\n\r\n", error);\r
- writer.Flush();\r
- client.Send(((MemoryStream)writer.BaseStream).ToArray());\r
+ header.Write("HTTP/1.1 200 OK\r\n");\r
+ header.Write("Server: KancolleSniffer\r\n");\r
+ header.Write("Date: {0:R}\r\n", DateTime.Now);\r
+ header.Write("Content-Type: application/x-ns-proxy-autoconfig\r\n");\r
+ header.Write("Connection: close\r\n\r\n");\r
+ header.Flush();\r
+ client.Send(((MemoryStream)header.BaseStream).ToArray());\r
}\r
+ var pacFile = @"\r
+function FindProxyForURL(url, host) {\r
+ if(isInNet(host, ""203.104.209.71"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.87"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.184.16"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.187.205"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.187.229"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.134"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.167"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.248.135"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.7"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.39"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.71"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.103"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.135"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.167"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.215"", ""255.255.255.255"") ||\r
+ isInNet(host, ""125.6.189.247"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.23"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.39"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.55"", ""255.255.255.255"") ||\r
+ isInNet(host, ""203.104.209.102"", ""255.255.255.255"")) {\r
+ return ""PROXY 127.0.0.1:8080"";\r
+ }\r
+ else {\r
+ return ""DIRECT"";\r
+ }\r
+}".Replace("8080", port.ToString());\r
+ client.Send(Encoding.ASCII.GetBytes(pacFile));\r
}\r
+ }\r
\r
- private void SendJsonData(Socket client, string path)\r
+ public static class BattleLogProcessor\r
+ {\r
+ public static IEnumerable<string> Process(string[] data)\r
{\r
- var header = new StreamWriter(new MemoryStream(), Encoding.ASCII);\r
- header.Write("HTTP/1.1 200 OK\r\n");\r
- header.Write("Server: KancolleSniffer\r\n");\r
- header.Write("Date: {0:R}\r\n", DateTime.Now);\r
- header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS");\r
- header.Write("Connection: close\r\n\r\n");\r
- header.Flush();\r
- client.Send(((MemoryStream)header.BaseStream).ToArray());\r
+ if (data.Length == 35)\r
+ data = data.Concat(Enumerable.Repeat("", 3)).ToArray();\r
+ if (data.Length != 38)\r
+ return data;\r
+ if (data[5] == "T字戦(有利)")\r
+ data[5] = "T字有利";\r
+ if (data[5] == "T字戦(不利)")\r
+ data[5] = "T字不利";\r
+ if (data[6].EndsWith("航行序列"))\r
+ data[6] = data[6].Substring(0, 4);\r
+ if (data[7].EndsWith("航行序列"))\r
+ data[7] = data[7].Substring(0, 4);\r
+ data[37] = ShortenAirBattleResult(data[37]);\r
+ return AddDamagedShip(data);\r
+ }\r
\r
- var encoding = Encoding.GetEncoding("Shift_JIS");\r
- client.Send(encoding.GetBytes("{ \"data\": [\n"));\r
- var delimiter = "";\r
- foreach (var line in File.ReadLines(path.Replace(".json", ".csv"), encoding).Skip(1))\r
+ private static string ShortenAirBattleResult(string result)\r
+ {\r
+ switch (result)\r
{\r
- client.Send(encoding.GetBytes(delimiter + "[\"" + string.Join("\",\"", line.Split(',')) + "\"]"));\r
- delimiter = ",\n";\r
+ case "制空均衡":\r
+ return "均衡";\r
+ case "制空権確保":\r
+ return "確保";\r
+ case "航空優勢":\r
+ return "優勢";\r
+ case "航空劣勢":\r
+ return "劣勢";\r
+ case "制空権喪失":\r
+ return "喪失";\r
+ default:\r
+ return "";\r
}\r
- client.Send(encoding.GetBytes("]}\n"));\r
}\r
\r
- private void SendFile(Socket client, string path, string mime)\r
+ private static IEnumerable<string> AddDamagedShip(string[] data)\r
{\r
- using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
+ var damaged = new List<string>();\r
+ for (var i = 11; i < 11 + 12; i += 2)\r
{\r
- writer.Write("HTTP/1.1 200 OK\r\n");\r
- writer.Write("Server: KancolleSniffer\r\n");\r
- writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
- writer.Write("Content-Length: {0}\r\n", new FileInfo(path).Length);\r
- writer.Write("Content-Type: {0}\r\n", mime);\r
- writer.Write("Connection: close\r\n\r\n");\r
- writer.Flush();\r
- client.SendFile(path, ((MemoryStream)writer.BaseStream).ToArray(), null,\r
- TransmitFileOptions.UseDefaultWorkerThread);\r
+ if (data[i] == "")\r
+ continue;\r
+ var ship = data[i] = StripKana(data[i]);\r
+ var hp = data[i + 1];\r
+ try\r
+ {\r
+ if (ship.Contains("・"))\r
+ {\r
+ var ships = ship.Split('・');\r
+ var hps = hp.Split('・');\r
+ var nowMax = hps[0].Split('/').Select(int.Parse).ToArray();\r
+ if (ShipStatus.CalcDamage(nowMax[0], nowMax[1]) == ShipStatus.Damage.Badly)\r
+ damaged.Add(ships[0]);\r
+ nowMax = hps[1].Split('/').Select(int.Parse).ToArray();\r
+ if (ShipStatus.CalcDamage(nowMax[0], nowMax[1]) == ShipStatus.Damage.Badly)\r
+ damaged.Add(ships[1]);\r
+ }\r
+ else\r
+ {\r
+ var nowMax = hp.Split('/').Select(int.Parse).ToArray();\r
+ if (ShipStatus.CalcDamage(nowMax[0], nowMax[1]) == ShipStatus.Damage.Badly)\r
+ damaged.Add(ship);\r
+ }\r
+ }\r
+ catch (FormatException)\r
+ {\r
+ return data;\r
+ }\r
}\r
+ return data.Take(23).Concat(new[] { string.Join("・", damaged) }).Concat(data.Skip(23));\r
}\r
\r
- public void Stop()\r
+ private static readonly Regex Kana = new Regex(@"\([^)]+\)\(", RegexOptions.Compiled);\r
+\r
+ private static string StripKana(string name)\r
{\r
- _listener.Stop();\r
- _thread.Join();\r
+ return Kana.Replace(name, "(");\r
}\r
}\r
}
\ No newline at end of file