OSDN Git Service

一覧の分類で艦種の選択ができないのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Log / Logger.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Globalization;\r
18 using System.IO;\r
19 using System.Linq;\r
20 using System.Text;\r
21 using KancolleSniffer.Model;\r
22 using KancolleSniffer.Util;\r
23 \r
24 namespace KancolleSniffer.Log\r
25 {\r
26     [Flags]\r
27     public enum LogType\r
28     {\r
29         None = 0,\r
30         Mission = 1,\r
31         Battle = 1 << 1,\r
32         Material = 1 << 2,\r
33         CreateItem = 1 << 3,\r
34         CreateShip = 1 << 4,\r
35         RemodelSlot = 1 << 5,\r
36         Achievement = 1 << 6,\r
37         All = (1 << 7) - 1\r
38     }\r
39 \r
40     public class Logger\r
41     {\r
42         private LogType _logType;\r
43         private readonly ShipInfo _shipInfo;\r
44         private readonly ItemInfo _itemInfo;\r
45         private Action<string, string, string> _writer;\r
46         private Func<DateTime> _nowFunc;\r
47         public const string DateTimeFormat = @"yyyy\-MM\-dd HH\:mm\:ss";\r
48         private dynamic _basic;\r
49         private int _kdockId;\r
50         private DateTime _prevTime;\r
51         private int[] _currentMaterial = new int[Enum.GetValues(typeof(Material)).Length];\r
52         private int _materialLogInterval = 10;\r
53         private int _lastExp = -1;\r
54         private DateTime _lastDate;\r
55         private DateTime _endOfMonth;\r
56         private DateTime _nextDate;\r
57         private readonly BattleLogger _battleLogger;\r
58 \r
59         public int MaterialLogInterval\r
60         {\r
61             set => _materialLogInterval = value;\r
62         }\r
63 \r
64         public string OutputDir\r
65         {\r
66             set => _writer = new LogWriter(value).Write;\r
67         }\r
68 \r
69         public static string FormatDateTime(DateTime date)\r
70         {\r
71             return date.ToString(DateTimeFormat, CultureInfo.InvariantCulture);\r
72         }\r
73 \r
74         public Logger(ShipInfo shipInfo, ItemInfo itemInfo, BattleInfo battleInfo)\r
75         {\r
76             _shipInfo = shipInfo;\r
77             _itemInfo = itemInfo;\r
78             _battleLogger = new BattleLogger(itemInfo, battleInfo, WriteNow);\r
79             _writer = new LogWriter().Write;\r
80             _nowFunc = () => DateTime.Now;\r
81         }\r
82 \r
83         public void EnableLog(LogType type)\r
84         {\r
85             _logType = type;\r
86         }\r
87 \r
88         public void SetWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
89         {\r
90             _writer = writer;\r
91             _nowFunc = nowFunc;\r
92         }\r
93 \r
94         private void WriteNow(string name, string log, string header)\r
95         {\r
96             Write(name, _nowFunc(), log, header);\r
97         }\r
98 \r
99         private void Write(string name, DateTime time, string log, string header)\r
100         {\r
101             _writer(name, FormatDateTime(time) + "," + log, header);\r
102         }\r
103 \r
104         public void FlashLog()\r
105         {\r
106             FlashAchievementLog();\r
107         }\r
108 \r
109         public void InspectMapInfoMaster(dynamic json)\r
110         {\r
111             _battleLogger.InspectMapInfoMaster(json);\r
112         }\r
113 \r
114         public void InspectMissionResult(dynamic json)\r
115         {\r
116             var r = (int)json.api_clear_result;\r
117             var resStr = r == 2 ? "大成功" : r == 1 ? "成功" : "失敗";\r
118             var material = new int[8];\r
119             if (r != 0)\r
120                 ((int[])json.api_get_material).CopyTo(material, 0);\r
121             foreach (var i in new[] {1, 2})\r
122             {\r
123                 var attr = "api_get_item" + i;\r
124                 if (!json.IsDefined(attr))\r
125                     continue;\r
126                 var count = (int)json[attr].api_useitem_count;\r
127                 var flag = ((int[])json.api_useitem_flag)[i - 1];\r
128                 switch (flag)\r
129                 {\r
130                     case 1:\r
131                         material[(int)Material.Bucket] = count;\r
132                         break;\r
133                     case 2:\r
134                         material[(int)Material.Burner + 2] = count; // 高速建造材と開発資材が反対なのでいつか直す\r
135                         break;\r
136                     case 3:\r
137                         material[(int)Material.Development - 2] = count;\r
138                         break;\r
139                     case 4:\r
140                         if ((int)json[attr].api_useitem_id == 4)\r
141                             material[(int)Material.Screw] = count;\r
142                         break;\r
143                 }\r
144             }\r
145             if ((_logType & LogType.Mission) != 0)\r
146             {\r
147                 WriteNow("遠征報告書",\r
148                     string.Join(",",\r
149                         resStr, json.api_quest_name, string.Join(",", material)),\r
150                     "日付,結果,遠征,燃料,弾薬,鋼材,ボーキ,開発資材,高速修復材,高速建造材,改修資材");\r
151             }\r
152         }\r
153 \r
154         public void InspectMapStart(dynamic json)\r
155         {\r
156             if ((_logType & LogType.Battle) != 0)\r
157                 _battleLogger.InspectMapStart(json);\r
158             if ((_logType & LogType.Material) != 0)\r
159                 WriteMaterialLog(_nowFunc());\r
160         }\r
161 \r
162         public void InspectMapNext(dynamic json)\r
163         {\r
164             if ((_logType & LogType.Achievement) != 0 && json.api_get_eo_rate() && (int)json.api_get_eo_rate != 0)\r
165             {\r
166                 WriteNow("戦果", _lastExp + "," + (int)json.api_get_eo_rate, "日付,経験値,EO");\r
167             }\r
168             if ((_logType & LogType.Battle) != 0)\r
169                 _battleLogger.InspectMapNext(json);\r
170         }\r
171 \r
172         public void InspectClearItemGet(dynamic json)\r
173         {\r
174             if ((_logType & LogType.Achievement) == 0)\r
175                 return;\r
176             if (!json.api_bounus())\r
177                 return;\r
178             foreach (var entry in json.api_bounus)\r
179             {\r
180                 if (entry.api_type != 18)\r
181                     continue;\r
182                 WriteNow("戦果", _lastExp + "," + (int)entry.api_count, "日付,経験値,EO");\r
183                 break;\r
184             }\r
185         }\r
186 \r
187         public void InspectBattleResult(dynamic result)\r
188         {\r
189             if ((_logType & LogType.Achievement) != 0 && result.api_get_exmap_rate())\r
190             {\r
191                 var rate = result.api_get_exmap_rate is string\r
192                     ? int.Parse(result.api_get_exmap_rate)\r
193                     : (int)result.api_get_exmap_rate;\r
194                 if (rate != 0)\r
195                 {\r
196                     WriteNow("戦果", _lastExp + "," + rate, "日付,経験値,EO");\r
197                 }\r
198             }\r
199             if ((_logType & LogType.Battle) != 0)\r
200                 _battleLogger.InspectBattleResult(result);\r
201         }\r
202 \r
203         public void InspectBasic(dynamic json)\r
204         {\r
205             _basic = json;\r
206             if ((_logType & LogType.Achievement) == 0)\r
207                 return;\r
208             var now = _nowFunc();\r
209             var exp = (int)json.api_experience;\r
210             var isNewMonth = _endOfMonth == DateTime.MinValue || now.CompareTo(_endOfMonth) >= 0;\r
211             var isNewDate = _nextDate == DateTime.MinValue || now.CompareTo(_nextDate) >= 0;\r
212             if (isNewDate || isNewMonth)\r
213             {\r
214                 if (_lastDate != DateTime.MinValue)\r
215                 {\r
216                     Write("戦果", _lastDate, _lastExp + ",0", "日付,経験値,EO");\r
217                 }\r
218                 Write("戦果", now, exp + ",0", "日付,経験値,EO");\r
219                 if (isNewMonth)\r
220                 {\r
221                     _endOfMonth = new DateTime(now.Year, now.Month, DateTime.DaysInMonth(now.Year, now.Month),\r
222                         22, 0, 0);\r
223                     if (_endOfMonth.CompareTo(now) <= 0)\r
224                     {\r
225                         var days = _endOfMonth.Month == 12\r
226                             ? DateTime.DaysInMonth(_endOfMonth.Year + 1, 1)\r
227                             : DateTime.DaysInMonth(_endOfMonth.Year, _endOfMonth.Month);\r
228                         _endOfMonth = _endOfMonth.AddDays(days);\r
229                     }\r
230                 }\r
231                 _nextDate = new DateTime(now.Year, now.Month, now.Day, 2, 0, 0);\r
232                 if (now.Hour >= 2)\r
233                     _nextDate = _nextDate.AddDays(1);\r
234                 if (_nextDate.Day == 1)\r
235                     _nextDate = _nextDate.AddDays(1);\r
236             }\r
237             _lastDate = now;\r
238             _lastExp = exp;\r
239         }\r
240 \r
241         private void FlashAchievementLog()\r
242         {\r
243             if ((_logType & LogType.Achievement) == 0)\r
244                 return;\r
245             if (_lastDate != DateTime.MinValue)\r
246             {\r
247                 Write("戦果", _lastDate, _lastExp + ",0", "日付,経験値,EO");\r
248             }\r
249         }\r
250 \r
251         public void InspectCreateItem(string request, dynamic json)\r
252         {\r
253             if ((_logType & LogType.CreateItem) == 0)\r
254                 return;\r
255             var values = HttpUtility.ParseQueryString(request);\r
256             foreach (var spec in CreateSpecList(json))\r
257             {\r
258                 WriteNow("開発報告書",\r
259                     string.Join(",", spec.Name, spec.TypeName,\r
260                         values["api_item1"], values["api_item2"], values["api_item3"], values["api_item4"],\r
261                         Secretary(), _basic.api_level),\r
262                     "日付,開発装備,種別,燃料,弾薬,鋼材,ボーキ,秘書艦,司令部Lv");\r
263             }\r
264         }\r
265 \r
266         private IEnumerable<ItemSpec> CreateSpecList(dynamic json)\r
267         {\r
268             var fail = new ItemSpec\r
269             {\r
270                 Name = "失敗",\r
271                 TypeName = ""\r
272             };\r
273             if (json.api_get_items())\r
274             {\r
275                 return ((dynamic[])json.api_get_items).Select(entry =>\r
276                     (int)entry.api_slotitem_id != -1 ? _itemInfo.GetSpecByItemId((int)entry.api_slotitem_id) : fail);\r
277             }\r
278             return new[]\r
279             {\r
280                 json.api_slot_item()\r
281                     ? _itemInfo.GetSpecByItemId((int)json.api_slot_item.api_slotitem_id)\r
282                     : fail\r
283             };\r
284         }\r
285 \r
286         public void InspectCreateShip(string request)\r
287         {\r
288             var values = HttpUtility.ParseQueryString(request);\r
289             _kdockId = int.Parse(values["api_kdock_id"]);\r
290         }\r
291 \r
292         public void InspectKDock(dynamic json)\r
293         {\r
294             if ((_logType & LogType.CreateShip) == 0 || _basic == null || _kdockId == 0)\r
295                 return;\r
296             var kdock = ((dynamic[])json).First(e => e.api_id == _kdockId);\r
297             var material = Enumerable.Range(1, 5).Select(i => (int)kdock["api_item" + i]).ToArray();\r
298             var ship = _shipInfo.GetSpec((int)kdock.api_created_ship_id);\r
299             var avail = ((dynamic[])json).Count(e => (int)e.api_state == 0);\r
300             WriteNow("建造報告書",\r
301                 string.Join(",", material.First() >= 1500 ? "大型艦建造" : "通常艦建造",\r
302                     ship.Name, ship.ShipTypeName, string.Join(",", material), avail, Secretary(), _basic.api_level),\r
303                 "日付,種類,名前,艦種,燃料,弾薬,鋼材,ボーキ,開発資材,空きドック,秘書艦,司令部Lv");\r
304             _kdockId = 0;\r
305         }\r
306 \r
307         private string Secretary()\r
308         {\r
309             var ship = _shipInfo.Fleets[0].Ships[0];\r
310             return ship.Name + "(" + ship.Level + ")";\r
311         }\r
312 \r
313         public void InspectMaterial(dynamic json)\r
314         {\r
315             if ((_logType & LogType.Material) == 0)\r
316                 return;\r
317             foreach (var e in json)\r
318                 _currentMaterial[(int)e.api_id - 1] = (int)e.api_value;\r
319             var now = _nowFunc();\r
320             if (now - _prevTime < TimeSpan.FromMinutes(_materialLogInterval))\r
321                 return;\r
322             WriteMaterialLog(now);\r
323         }\r
324 \r
325         private void WriteMaterialLog(DateTime now)\r
326         {\r
327             _prevTime = now;\r
328             Write("資材ログ", now,\r
329                 string.Join(",", _currentMaterial),\r
330                 "日付,燃料,弾薬,鋼材,ボーキ,高速建造材,高速修復材,開発資材,改修資材");\r
331         }\r
332 \r
333         public void SetCurrentMaterial(int[] material)\r
334         {\r
335             _currentMaterial = material;\r
336         }\r
337 \r
338         public void InspectRemodelSlot(string request, dynamic json)\r
339         {\r
340             if ((_logType & LogType.RemodelSlot) == 0)\r
341                 return;\r
342             var values = HttpUtility.ParseQueryString(request);\r
343             var id = int.Parse(values["api_slot_id"]);\r
344             var name = _itemInfo.GetName(id);\r
345             var level = _itemInfo.GetStatus(id).Level;\r
346             var success = (int)json.api_remodel_flag == 1 ? "○" : "×";\r
347             var certain = int.Parse(values["api_certain_flag"]) == 1 ? "○" : "";\r
348             var useName = "";\r
349             var useNum = "";\r
350             if (json.api_use_slot_id())\r
351             {\r
352                 var use = (int[])json.api_use_slot_id;\r
353                 useName = _itemInfo.GetName(use[0]);\r
354                 useNum = use.Length.ToString();\r
355             }\r
356             var after = (int[])json.api_after_material;\r
357             var diff = new int[after.Length];\r
358             for (var i = 0; i < after.Length; i++)\r
359                 diff[i] = _currentMaterial[i] - after[i];\r
360             var ship1 = Secretary();\r
361             var ship2 = "";\r
362             var ships = _shipInfo.Fleets[0].Ships;\r
363             if (!ships[1].Empty)\r
364                 ship2 = ships[1].Name + "(" + ships[1].Level + ")";\r
365             WriteNow("改修報告書",\r
366                 string.Join(",", name, level, success, certain, useName, useNum,\r
367                     diff[(int)Material.Fuel], diff[(int)Material.Bullet], diff[(int)Material.Steal],\r
368                     diff[(int)Material.Bauxite],\r
369                     diff[(int)Material.Development], diff[(int)Material.Screw],\r
370                     ship1, ship2),\r
371                 "日付,改修装備,レベル,成功,確実化,消費装備,消費数,燃料,弾薬,鋼材,ボーキ,開発資材,改修資材,秘書艦,二番艦");\r
372         }\r
373     }\r
374 \r
375     public class LogWriter\r
376     {\r
377         private readonly IFile _file;\r
378         private readonly string _outputDir;\r
379 \r
380         public interface IFile\r
381         {\r
382             string ReadAllText(string path);\r
383             void AppendAllText(string path, string text);\r
384             void Delete(string path);\r
385             bool Exists(string path);\r
386         }\r
387 \r
388         private class FileWrapper : IFile\r
389         {\r
390             // Shift_JISでないとExcelで文字化けする\r
391             private readonly Encoding _encoding = Encoding.GetEncoding("Shift_JIS");\r
392 \r
393             public string ReadAllText(string path) => File.ReadAllText(path, _encoding);\r
394 \r
395             public void AppendAllText(string path, string text)\r
396             {\r
397                 File.AppendAllText(path, text, _encoding);\r
398             }\r
399 \r
400             public void Delete(string path)\r
401             {\r
402                 File.Delete(path);\r
403             }\r
404 \r
405             public bool Exists(string path) => File.Exists(path);\r
406         }\r
407 \r
408         public LogWriter(string outputDir = null, IFile file = null)\r
409         {\r
410             _outputDir = outputDir ?? AppDomain.CurrentDomain.BaseDirectory;\r
411             _file = file ?? new FileWrapper();\r
412         }\r
413 \r
414         public void Write(string file, string s, string header)\r
415         {\r
416             var path = Path.Combine(_outputDir, file);\r
417             var csv = path + ".csv";\r
418             var tmp = path + ".tmp";\r
419             if (_file.Exists(tmp))\r
420             {\r
421                 try\r
422                 {\r
423                     _file.AppendAllText(csv, _file.ReadAllText(tmp));\r
424                     _file.Delete(tmp);\r
425                 }\r
426                 catch (IOException)\r
427                 {\r
428                 }\r
429             }\r
430             if (!_file.Exists(csv))\r
431                 s = header + "\r\n" + s;\r
432             foreach (var f in new[] {csv, tmp})\r
433             {\r
434                 try\r
435                 {\r
436                     _file.AppendAllText(f, s + "\r\n");\r
437                     break;\r
438                 }\r
439                 catch (IOException e)\r
440                 {\r
441                     if (f == tmp)\r
442                         throw new LogIOException("報告書の出力中にエラーが発生しました。", e);\r
443                 }\r
444             }\r
445         }\r
446     }\r
447 \r
448     public class LogIOException : Exception\r
449     {\r
450         public LogIOException(string message, Exception inner) : base(message, inner)\r
451         {\r
452         }\r
453     }\r
454 }