OSDN Git Service

初戦がボスのときに報告書にボスとして記録されないのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Logger.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Collections.Generic;\r
20 using System.IO;\r
21 using System.Text;\r
22 using System.Linq;\r
23 using System.Web;\r
24 using System.Windows.Forms;\r
25 \r
26 namespace KancolleSniffer\r
27 {\r
28     [Flags]\r
29     public enum LogType\r
30     {\r
31         None = 0,\r
32         Mission = 1,\r
33         Battle = 2,\r
34         Material = 4,\r
35         CreateItem = 8,\r
36         CreateShip = 16,\r
37         All = 31,\r
38     }\r
39 \r
40     public class Logger\r
41     {\r
42         private LogType _logType;\r
43         private readonly ShipMaster _shipMaster;\r
44         private readonly ShipInfo _shipInfo;\r
45         private readonly ItemInfo _itemInfo;\r
46         private Action<string, string, string> _writer;\r
47         private Func<DateTime> _nowFunc;\r
48         public const string DateTimeFormat = @"yyyy\-MM\-dd HH\:mm\:ss";\r
49         private dynamic _battle;\r
50         private dynamic _map;\r
51         private dynamic _basic;\r
52         private int _kdockId;\r
53         private DateTime _prevTime;\r
54         private int _materialLogInterval = 10;\r
55         private bool _start;\r
56 \r
57         public int MaterialLogInterval\r
58         {\r
59             set { _materialLogInterval = value; }\r
60         }\r
61 \r
62         public string OutputDir\r
63         {\r
64             set { _writer = new LogWriter(value).Write; }\r
65         }\r
66 \r
67         public Logger(ShipMaster master, ShipInfo ship, ItemInfo item)\r
68         {\r
69             _shipMaster = master;\r
70             _shipInfo = ship;\r
71             _itemInfo = item;\r
72             _writer = new LogWriter().Write;\r
73             _nowFunc = () => DateTime.Now;\r
74         }\r
75 \r
76         public void EnableLog(LogType type)\r
77         {\r
78             _logType = type;\r
79         }\r
80 \r
81         public void SetWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
82         {\r
83             _writer = writer;\r
84             _nowFunc = nowFunc;\r
85         }\r
86 \r
87         public void InspectMissionResult(dynamic json)\r
88         {\r
89             var r = (int)json.api_clear_result;\r
90             var rstr = r == 2 ? "大成功" : r == 1 ? "成功" : "失敗";\r
91             var material = new int[7];\r
92             if (r != 0)\r
93                 ((int[])json.api_get_material).CopyTo(material, 0);\r
94             foreach (var i in new[] {1, 2})\r
95             {\r
96                 var attr = "api_get_item" + i;\r
97                 if (!json.IsDefined(attr) || json[attr].api_useitem_id != -1)\r
98                     continue;\r
99                 var count = (int)json[attr].api_useitem_count;\r
100                 var flag = ((int[])json.api_useitem_flag)[i - 1];\r
101                 if (flag == 1)\r
102                     material[(int)Material.Bucket] = count;\r
103                 else if (flag == 2)\r
104                     material[(int)Material.Burner + 2] = count; // 高速建造材と開発資材が反対なのでいつか直す\r
105                 else if (flag == 3)\r
106                     material[(int)Material.Development - 2] = count;\r
107             }\r
108             if ((_logType & LogType.Mission) != 0)\r
109             {\r
110                 _writer("遠征報告書",\r
111                     string.Join(",", _nowFunc().ToString(DateTimeFormat),\r
112                         rstr, json.api_quest_name, string.Join(",", material)),\r
113                     "日付,結果,遠征,燃料,弾薬,鋼材,ボーキ,開発資材,高速修復材,高速建造材");\r
114             }\r
115         }\r
116 \r
117         public void InspectMapStart(dynamic json)\r
118         {\r
119             _start = true;\r
120             _map = json;\r
121         }\r
122 \r
123         public void InspectMapNext(dynamic json)\r
124         {\r
125             _map = json;\r
126         }\r
127 \r
128         public void InspectBattle(dynamic json)\r
129         {\r
130             if (_battle != null) // 通常の夜戦は無視する\r
131                 return;\r
132             _battle = json;\r
133         }\r
134 \r
135         public void InspectBattleResult(dynamic result)\r
136         {\r
137             if ((_logType & LogType.Battle) == 0 || _map == null || _battle == null)\r
138             {\r
139                 _map = _battle = null;\r
140                 return;\r
141             }\r
142             var fships = new List<string>();\r
143             var deck = _shipInfo.GetDeck(_battle.api_dock_id() ? (int)_battle.api_dock_id - 1 : 0);\r
144             fships.AddRange(deck.Select(id =>\r
145             {\r
146                 if (id == -1)\r
147                     return ",";\r
148                 var s = _shipInfo[id];\r
149                 return string.Format("{0}(Lv{1}),{2}/{3}", s.Name, s.Level, s.NowHp, s.MaxHp);\r
150             }));\r
151             var edeck = ((int[])_battle.api_ship_ke).Skip(1).ToArray();\r
152             var enowhp = ((int[])_battle.api_nowhps).Skip(7).ToArray();\r
153             var emaxhp = ((int[])_battle.api_maxhps).Skip(7).ToArray();\r
154             var eships = new List<string>();\r
155             for (var i = 0; i < edeck.Count(); i++)\r
156             {\r
157                 eships.Add(edeck[i] == -1\r
158                     ? ","\r
159                     : string.Format("{0},{1}/{2}", _shipMaster[edeck[i]].Name, enowhp[i], emaxhp[i]));\r
160             }\r
161             var cell = (int)_map.api_no;\r
162             var boss = "";\r
163             if (_start)\r
164                 boss = "出撃";\r
165             if (cell == (int)_map.api_bosscell_no || (int)_map.api_event_id == 5)\r
166                 boss = _start ? "出撃&ボス" : "ボス";\r
167             _writer("海戦・ドロップ報告書", string.Join(",", _nowFunc().ToString(DateTimeFormat),\r
168                 result.api_quest_name,\r
169                 cell, boss,\r
170                 result.api_win_rank,\r
171                 BattleFormationName((int)_battle.api_formation[2]),\r
172                 FormationName(_battle.api_formation[0]),\r
173                 FormationName(_battle.api_formation[1]),\r
174                 result.api_enemy_info.api_deck_name,\r
175                 result.api_get_ship() ? result.api_get_ship.api_ship_type : "",\r
176                 result.api_get_ship() ? result.api_get_ship.api_ship_name : "",\r
177                 string.Join(",", fships),\r
178                 string.Join(",", eships)),\r
179                 "日付,海域,マス,ボス,ランク,艦隊行動,味方陣形,敵陣形,敵艦隊,ドロップ艦種,ドロップ艦娘," +\r
180                 "味方艦1,味方艦1HP,味方艦2,味方艦2HP,味方艦3,味方艦3HP,味方艦4,味方艦4HP,味方艦5,味方艦5HP,味方艦6,味方艦6HP," +\r
181                 "敵艦1,敵艦1HP,敵艦2,敵艦2HP,敵艦3,敵艦3HP,敵艦4,敵艦4HP,敵艦5,敵艦5HP,敵艦6,敵艦6HP"\r
182                 );\r
183             _map = _battle = null;\r
184             _start = false;\r
185         }\r
186 \r
187         private string FormationName(dynamic f)\r
188         {\r
189             if (f is string) // 連合艦隊のときは文字列\r
190                 f = int.Parse(f);\r
191             switch ((int)f)\r
192             {\r
193                 case 1:\r
194                     return "単縦陣";\r
195                 case 2:\r
196                     return "複縦陣";\r
197                 case 3:\r
198                     return "輪形陣";\r
199                 case 4:\r
200                     return "梯形陣";\r
201                 case 5:\r
202                     return "単横陣";\r
203                 case 11:\r
204                     return "第一警戒航行序列";\r
205                 case 12:\r
206                     return "第二警戒航行序列";\r
207                 case 13:\r
208                     return "第三警戒航行序列";\r
209                 case 14:\r
210                     return "第四警戒航行序列";\r
211                 default:\r
212                     return "単縦陣";\r
213             }\r
214         }\r
215 \r
216         private static String BattleFormationName(int f)\r
217         {\r
218             switch (f)\r
219             {\r
220                 case 1:\r
221                     return "同航戦";\r
222                 case 2:\r
223                     return "反航戦";\r
224                 case 3:\r
225                     return "T字戦(有利)";\r
226                 case 4:\r
227                     return "T字戦(不利)";\r
228                 default:\r
229                     return "同航戦";\r
230             }\r
231         }\r
232 \r
233         public void InspectBasic(dynamic json)\r
234         {\r
235             _basic = json;\r
236         }\r
237 \r
238         public void InspectCreateItem(string request, dynamic json)\r
239         {\r
240             if ((_logType & LogType.CreateItem) == 0)\r
241                 return;\r
242             var values = HttpUtility.ParseQueryString(request);\r
243             var name = "失敗";\r
244             var type = "";\r
245             if (json.api_slot_item())\r
246             {\r
247                 var spec = _itemInfo.GetSpecByItemId((int)json.api_slot_item.api_slotitem_id);\r
248                 name = spec.Name;\r
249                 type = spec.TypeName;\r
250             }\r
251             _writer("開発報告書",\r
252                 _nowFunc().ToString(DateTimeFormat) + "," +\r
253                 string.Join(",", name, type,\r
254                     values["api_item1"], values["api_item2"], values["api_item3"], values["api_item4"],\r
255                     Secretary(), _basic.api_level),\r
256                 "日付,開発装備,種別,燃料,弾薬,鋼材,ボーキ,秘書艦,司令部Lv");\r
257         }\r
258 \r
259         public void InspectCreateShip(string request)\r
260         {\r
261             var values = HttpUtility.ParseQueryString(request);\r
262             _kdockId = int.Parse(values["api_kdock_id"]);\r
263         }\r
264 \r
265         public void InspectKDock(dynamic json)\r
266         {\r
267             if ((_logType & LogType.CreateShip) == 0 || _basic == null || _kdockId == 0)\r
268                 return;\r
269             var kdock = ((dynamic[])json).First(e => e.api_id == _kdockId);\r
270             var material = Enumerable.Range(1, 5).Select(i => (int)kdock["api_item" + i]).ToArray();\r
271             var ship = _shipMaster[(int)kdock.api_created_ship_id];\r
272             var avail = ((dynamic[])json).Count(e => (int)e.api_state == 0);\r
273             _writer("建造報告書",\r
274                 _nowFunc().ToString(DateTimeFormat) + "," +\r
275                 string.Join(",", material.First() >= 1500 ? "大型艦建造" : "通常艦建造",\r
276                     ship.Name, ship.ShipTypeName, string.Join(",", material), avail, Secretary(), _basic.api_level),\r
277                 "日付,種類,名前,艦種,燃料,弾薬,鋼材,ボーキ,開発資材,空きドック,秘書艦,司令部Lv");\r
278             _kdockId = 0;\r
279         }\r
280 \r
281         private string Secretary()\r
282         {\r
283             var ship = _shipInfo.GetShipStatuses(0)[0];\r
284             return ship.Name + "(" + ship.Level + ")";\r
285         }\r
286 \r
287         public void InspectMaterial(dynamic json)\r
288         {\r
289             if ((_logType & LogType.Material) == 0)\r
290                 return;\r
291             var now = _nowFunc();\r
292             if (now - _prevTime < TimeSpan.FromMinutes(_materialLogInterval))\r
293                 return;\r
294             _prevTime = now;\r
295             var material = new int[8];\r
296             foreach (var e in json)\r
297                 material[(int)e.api_id - 1] = (int)e.api_value;\r
298             _writer("資材ログ",\r
299                 now.ToString(DateTimeFormat) + "," +\r
300                 string.Join(",", material),\r
301                 "日付,燃料,弾薬,鋼材,ボーキ,高速建造材,高速修復材,開発資材,改修資材");\r
302         }\r
303     }\r
304 \r
305     public class LogWriter\r
306     {\r
307         private readonly IFile _file;\r
308         private readonly string _outputDir;\r
309 \r
310         public interface IFile\r
311         {\r
312             string ReadAllText(string path);\r
313             void AppendAllText(string path, string text);\r
314             void Delete(string path);\r
315             bool Exists(string path);\r
316         }\r
317 \r
318         private class FileWrapper : IFile\r
319         {\r
320             // Shift_JISでないとExcelで文字化けする\r
321             private readonly Encoding _encoding = Encoding.GetEncoding("Shift_JIS");\r
322 \r
323             public string ReadAllText(string path)\r
324             {\r
325                 return File.ReadAllText(path, _encoding);\r
326             }\r
327 \r
328             public void AppendAllText(string path, string text)\r
329             {\r
330                 File.AppendAllText(path, text, _encoding);\r
331             }\r
332 \r
333             public void Delete(string path)\r
334             {\r
335                 File.Delete(path);\r
336             }\r
337 \r
338             public bool Exists(string path)\r
339             {\r
340                 return File.Exists(path);\r
341             }\r
342         }\r
343 \r
344         public LogWriter(string outputDir = null, IFile file = null)\r
345         {\r
346             _outputDir = outputDir ?? Path.GetDirectoryName(Application.ExecutablePath);\r
347             _file = file ?? new FileWrapper();\r
348         }\r
349 \r
350         public void Write(string file, string s, string header)\r
351         {\r
352             var path = Path.Combine(_outputDir, file);\r
353             var csv = path + ".csv";\r
354             var tmp = path + ".tmp";\r
355             if (_file.Exists(tmp))\r
356             {\r
357                 try\r
358                 {\r
359                     _file.AppendAllText(csv, _file.ReadAllText(tmp));\r
360                     _file.Delete(tmp);\r
361                 }\r
362                 catch (IOException)\r
363                 {\r
364                 }\r
365             }\r
366             if (!_file.Exists(csv))\r
367                 s = header + "\r\n" + s;\r
368             foreach (var f in new[] {csv, tmp})\r
369             {\r
370                 try\r
371                 {\r
372                     _file.AppendAllText(f, s + "\r\n");\r
373                     break;\r
374                 }\r
375                 catch (IOException)\r
376                 {\r
377                 }\r
378             }\r
379         }\r
380     }\r
381 }