OSDN Git Service

バージョン12.11の準備
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Main.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.ComponentModel;\r
4 using System.Diagnostics;\r
5 using System.Globalization;\r
6 using System.IO;\r
7 using System.Linq;\r
8 using System.Net;\r
9 using System.Text.RegularExpressions;\r
10 using System.Windows.Forms;\r
11 using DynaJson;\r
12 using KancolleSniffer.Forms;\r
13 using KancolleSniffer.Log;\r
14 using KancolleSniffer.Net;\r
15 using KancolleSniffer.Util;\r
16 using Microsoft.CSharp.RuntimeBinder;\r
17 \r
18 namespace KancolleSniffer\r
19 {\r
20     public class Main\r
21     {\r
22         private ProxyManager _proxyManager;\r
23         private Form _form;\r
24         private MainWindow _mainBehavior;\r
25         private readonly ErrorDialog _errorDialog = new ErrorDialog();\r
26         private ConfigDialog _configDialog;\r
27         private ErrorLog _errorLog;\r
28         private IEnumerator<string> _playLog;\r
29         private string _debugLogFile;\r
30         private bool _timerEnabled;\r
31         private readonly Timer _mainTimer = new Timer {Interval = 1000, Enabled = true};\r
32 \r
33         public TimeStep Step { get; } = new TimeStep();\r
34 \r
35         public Config Config { get; } = new Config();\r
36         public Sniffer Sniffer { get; } = new Sniffer();\r
37 \r
38         public static void Run()\r
39         {\r
40             new Main().RunInternal();\r
41         }\r
42 \r
43         private void RunInternal()\r
44         {\r
45             Config.Load();\r
46             _configDialog = new ConfigDialog(this);\r
47             var form = Config.Shape.StartsWith("横長") ? (Form)new HorizontalMainForm() : new VerticalMainForm();\r
48             _form = form;\r
49             _mainBehavior = new MainWindow(this, form);\r
50             _proxyManager = new ProxyManager(_form, Config);\r
51             _proxyManager.UpdatePacFile();\r
52             _errorLog = new ErrorLog(Sniffer);\r
53             Sniffer.RepeatingTimerController = _mainBehavior.Notifier;\r
54             LoadData();\r
55             ApplyConfig();\r
56             ApplySettings();\r
57             HttpProxy.AfterSessionComplete += HttpProxy_AfterSessionComplete;\r
58             _mainTimer.Tick += TimerTick;\r
59             Application.Run(_form);\r
60             Terminate();\r
61         }\r
62 \r
63         public void CheckVersionUpMain(LinkLabel guide)\r
64         {\r
65             CheckVersionUp((current, latest) =>\r
66             {\r
67                 if (latest == current)\r
68                     return;\r
69                 guide.Text = $"バージョン{latest}があります。";\r
70                 guide.LinkArea = new LinkArea(0, guide.Text.Length);\r
71                 guide.Click += (obj, ev) => { Process.Start("https://ja.osdn.net/rel/kancollesniffer/" + latest); };\r
72             });\r
73         }\r
74 \r
75         private readonly FileSystemWatcher _watcher = new FileSystemWatcher\r
76         {\r
77             Path = AppDomain.CurrentDomain.BaseDirectory,\r
78             NotifyFilter = NotifyFilters.LastWrite\r
79         };\r
80 \r
81         private readonly Timer _watcherTimer = new Timer {Interval = 1000};\r
82 \r
83         private void LoadData()\r
84         {\r
85             var target = "";\r
86             Sniffer.LoadState();\r
87             _watcher.SynchronizingObject = _form;\r
88             _watcherTimer.Tick += (sender, ev) =>\r
89             {\r
90                 _watcherTimer.Stop();\r
91                 switch (target)\r
92                 {\r
93                     case "status.xml":\r
94                         Sniffer.LoadState();\r
95                         break;\r
96                     case "TP.csv":\r
97                         Sniffer.AdditionalData.LoadTpSpec();\r
98                         break;\r
99                 }\r
100             };\r
101             _watcher.Changed += (sender, ev) =>\r
102             {\r
103                 target = ev.Name;\r
104                 _watcherTimer.Stop();\r
105                 _watcherTimer.Start();\r
106             };\r
107             _watcher.EnableRaisingEvents = true;\r
108         }\r
109 \r
110         private void HttpProxy_AfterSessionComplete(HttpProxy.Session session)\r
111         {\r
112             _form.BeginInvoke(new Action<HttpProxy.Session>(ProcessRequest), session);\r
113         }\r
114 \r
115         public class Session\r
116         {\r
117             public string Url { get; set; }\r
118             public string Request { get; set; }\r
119             public string Response { get; set; }\r
120 \r
121             public Session(string url, string request, string response)\r
122             {\r
123                 Url = url;\r
124                 Request = request;\r
125                 Response = response;\r
126             }\r
127 \r
128             public string[] Lines => new[] {Url, Request, Response};\r
129         }\r
130 \r
131         private void ProcessRequest(HttpProxy.Session session)\r
132         {\r
133             var url = session.Request.PathAndQuery;\r
134             if (!url.Contains("kcsapi/"))\r
135                 return;\r
136             var s = new Session(url, session.Request.BodyAsString, session.Response.BodyAsString);\r
137             Privacy.Remove(s);\r
138             if (s.Response == null || !s.Response.StartsWith("svdata="))\r
139             {\r
140                 WriteDebugLog(s);\r
141                 return;\r
142             }\r
143             s.Response = UnEscapeString(s.Response.Remove(0, "svdata=".Length));\r
144             WriteDebugLog(s);\r
145             ProcessRequestMain(s);\r
146         }\r
147 \r
148         private void ProcessRequestMain(Session s)\r
149         {\r
150             try\r
151             {\r
152                 var update = (Sniffer.Update)Sniffer.Sniff(s.Url, s.Request, JsonObject.Parse(s.Response));\r
153                 _mainBehavior.UpdateInfo(update);\r
154                 if (!Sniffer.Started)\r
155                     return;\r
156                 Step.SetNowIfNeeded();\r
157                 if ((update & Sniffer.Update.Timer) != 0)\r
158                     _timerEnabled = true;\r
159                 _errorLog.CheckBattleApi(s);\r
160             }\r
161 \r
162             catch (RuntimeBinderException e)\r
163             {\r
164                 if (_errorDialog.ShowDialog(_form,\r
165                     "艦これに仕様変更があったか、受信内容が壊れています。",\r
166                     _errorLog.GenerateErrorLog(s, e.ToString())) == DialogResult.Abort)\r
167                     Exit();\r
168             }\r
169             catch (LogIOException e)\r
170             {\r
171                 // ReSharper disable once PossibleNullReferenceException\r
172                 if (_errorDialog.ShowDialog(_form, e.Message, e.InnerException.ToString()) == DialogResult.Abort)\r
173                     Exit();\r
174             }\r
175             catch (BattleResultError)\r
176             {\r
177                 if (_errorDialog.ShowDialog(_form, "戦闘結果の計算に誤りがあります。",\r
178                     _errorLog.GenerateBattleErrorLog()) == DialogResult.Abort)\r
179                     Exit();\r
180             }\r
181             catch (Exception e)\r
182             {\r
183                 if (_errorDialog.ShowDialog(_form, "エラーが発生しました。",\r
184                     _errorLog.GenerateErrorLog(s, e.ToString())) == DialogResult.Abort)\r
185                     Exit();\r
186             }\r
187         }\r
188 \r
189         private void Exit()\r
190         {\r
191             _proxyManager.Shutdown();\r
192             Environment.Exit(1);\r
193         }\r
194 \r
195         private void WriteDebugLog(Session s)\r
196         {\r
197             if (_debugLogFile != null)\r
198             {\r
199                 File.AppendAllText(_debugLogFile,\r
200                     $"date: {DateTime.Now:g}\nurl: {s.Url}\nrequest: {s.Request}\nresponse: {s.Response ?? "(null)"}\n");\r
201             }\r
202         }\r
203 \r
204         private string UnEscapeString(string s)\r
205         {\r
206             try\r
207             {\r
208                 var rx = new Regex(@"\\[uU]([0-9A-Fa-f]{4})");\r
209                 return rx.Replace(s,\r
210                     match => ((char)int.Parse(match.Value.Substring(2), NumberStyles.HexNumber)).ToString());\r
211             }\r
212             catch (ArgumentException)\r
213             {\r
214                 return s;\r
215             }\r
216         }\r
217 \r
218         public async void CheckVersionUp(Action<string, string> action)\r
219         {\r
220             var current = string.Join(".", Application.ProductVersion.Split('.').Take(2));\r
221             try\r
222             {\r
223                 var latest = (await new WebClient().DownloadStringTaskAsync("http://kancollesniffer.osdn.jp/version"))\r
224                     .TrimEnd();\r
225                 try\r
226                 {\r
227                     action(current, latest);\r
228                 }\r
229                 catch (InvalidOperationException)\r
230                 {\r
231                 }\r
232             }\r
233             catch (WebException)\r
234             {\r
235             }\r
236         }\r
237 \r
238         private void ApplySettings()\r
239         {\r
240             ApplyDebugLogSetting();\r
241             ApplyLogSetting();\r
242             ApplyProxySetting();\r
243         }\r
244 \r
245         public void ApplyDebugLogSetting()\r
246         {\r
247             _debugLogFile = Config.DebugLogging ? Config.DebugLogFile : null;\r
248         }\r
249 \r
250         public bool ApplyProxySetting()\r
251         {\r
252             return _proxyManager.ApplyConfig();\r
253         }\r
254 \r
255         public void ApplyLogSetting()\r
256         {\r
257             LogServer.OutputDir = Config.Log.OutputDir;\r
258             LogServer.LogProcessor = new LogProcessor(Sniffer.Material.MaterialHistory, Sniffer.MapDictionary);\r
259             Sniffer.EnableLog(Config.Log.On ? LogType.All : LogType.None);\r
260             Sniffer.MaterialLogInterval = Config.Log.MaterialLogInterval;\r
261             Sniffer.LogOutputDir = Config.Log.OutputDir;\r
262         }\r
263 \r
264         public void SetPlayLog(string file)\r
265         {\r
266             try\r
267             {\r
268                 _playLog = File.ReadLines(file).GetEnumerator();\r
269             }\r
270             catch (FileNotFoundException)\r
271             {\r
272             }\r
273         }\r
274 \r
275         private void TimerTick(object sender, EventArgs ev)\r
276         {\r
277             if (_timerEnabled)\r
278             {\r
279                 try\r
280                 {\r
281                     Step.SetNow();\r
282                     _mainBehavior.UpdateTimers();\r
283                     _mainBehavior.Notifier.NotifyTimers();\r
284                     Step.SetPrev();\r
285                 }\r
286                 catch (Exception ex)\r
287                 {\r
288                     if (_errorDialog.ShowDialog(_form, "エラーが発生しました。", ex.ToString()) == DialogResult.Abort)\r
289                         Exit();\r
290                 }\r
291             }\r
292             if (_playLog == null || _configDialog.Visible)\r
293             {\r
294                 _mainBehavior.PlayLogSign.Visible = false;\r
295                 return;\r
296             }\r
297             PlayLog();\r
298         }\r
299 \r
300         public void ResetAchievement()\r
301         {\r
302             Sniffer.Achievement.Reset();\r
303             _mainBehavior.UpdateItemInfo();\r
304         }\r
305 \r
306         private void PlayLog()\r
307         {\r
308             var lines = new List<string>();\r
309             var sign = _mainBehavior.PlayLogSign;\r
310             foreach (var s in new[] {"url: ", "request: ", "response: "})\r
311             {\r
312                 do\r
313                 {\r
314                     if (!_playLog.MoveNext() || _playLog.Current == null)\r
315                     {\r
316                         sign.Visible = false;\r
317                         return;\r
318                     }\r
319                 } while (!_playLog.Current.StartsWith(s));\r
320                 lines.Add(_playLog.Current.Substring(s.Length));\r
321             }\r
322             sign.Visible = !sign.Visible;\r
323             ProcessRequestMain(new Session(lines[0], lines[1], lines[2]));\r
324         }\r
325 \r
326         public void ShowConfigDialog()\r
327         {\r
328             if (_configDialog.ShowDialog(_form) == DialogResult.OK)\r
329             {\r
330                 Config.Save();\r
331                 ApplyConfig();\r
332                 _mainBehavior.Notifier.StopRepeatingTimer(_configDialog.RepeatSettingsChanged);\r
333             }\r
334         }\r
335 \r
336         private void ApplyConfig()\r
337         {\r
338             Sniffer.ShipCounter.Margin = Config.MarginShips;\r
339             Sniffer.ItemCounter.Margin = Config.MarginEquips;\r
340             _mainBehavior.Notifier.NotifyShipItemCount();\r
341             Sniffer.Achievement.ResetHours = Config.ResetHours;\r
342             Sniffer.WarnBadDamageWithDameCon = Config.WarnBadDamageWithDameCon;\r
343             _mainBehavior.ApplyConfig();\r
344         }\r
345 \r
346         public IEnumerable<Control> Controls =>\r
347             new Control[] {_errorDialog, _configDialog, _configDialog.NotificationConfigDialog};\r
348 \r
349         private void Terminate()\r
350         {\r
351             _proxyManager.Shutdown();\r
352             Config.Save();\r
353             Sniffer.FlashLog();\r
354             Sniffer.SaveState();\r
355         }\r
356 \r
357         public void ShowReport()\r
358         {\r
359             Process.Start("http://localhost:" + Config.Proxy.Listen + "/");\r
360         }\r
361 \r
362         public void StartCapture()\r
363         {\r
364             try\r
365             {\r
366                 var proc = new ProcessStartInfo("BurageSnap.exe") {WorkingDirectory = "Capture"};\r
367                 Process.Start(proc);\r
368             }\r
369             catch (FileNotFoundException)\r
370             {\r
371             }\r
372             catch (Win32Exception)\r
373             {\r
374             }\r
375         }\r
376     }\r
377 }