-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.IO;\r
-using System.Text;\r
using System.Linq;\r
-using System.Web;\r
-using System.Windows.Forms;\r
+using System.Text;\r
\r
namespace KancolleSniffer\r
{\r
public enum LogType\r
{\r
None = 0,\r
- Mission = 1,\r
- Battle = 2,\r
- Material = 4,\r
- CreateItem = 8,\r
- CreateShip = 16,\r
- All = 31,\r
+ Mission = 1 << 0,\r
+ Battle = 1 << 1,\r
+ Material = 1 << 2,\r
+ CreateItem = 1 << 3,\r
+ CreateShip = 1 << 4,\r
+ RemodelSlot = 1 << 5,\r
+ Achivement = 1 << 6,\r
+ All = (1 << 7) - 1\r
}\r
\r
public class Logger\r
{\r
private LogType _logType;\r
- private readonly ShipMaster _shipMaster;\r
private readonly ShipInfo _shipInfo;\r
private readonly ItemInfo _itemInfo;\r
+ private readonly BattleInfo _battleInfo;\r
private Action<string, string, string> _writer;\r
private Func<DateTime> _nowFunc;\r
- private const string DateTimeFormat = @"yyyy\-MM\-dd HH\:mm\:ss";\r
+ public const string DateTimeFormat = @"yyyy\-MM\-dd HH\:mm\:ss";\r
private dynamic _battle;\r
private dynamic _map;\r
private dynamic _basic;\r
private int _kdockId;\r
private DateTime _prevTime;\r
+ private int[] _currentMaterial = new int[Enum.GetValues(typeof(Material)).Length];\r
+ private int _materialLogInterval = 10;\r
+ private bool _start;\r
+ private int _lastExp = -1;\r
+ private DateTime _lastDate;\r
+ private DateTime _endOfMonth;\r
+ private DateTime _nextDate;\r
+\r
+ public int MaterialLogInterval\r
+ {\r
+ set => _materialLogInterval = value;\r
+ }\r
+\r
+ public string OutputDir\r
+ {\r
+ set => _writer = new LogWriter(value).Write;\r
+ }\r
\r
- public Logger(ShipMaster master, ShipInfo ship, ItemInfo item)\r
+ public Logger(ShipInfo ship, ItemInfo item, BattleInfo battle)\r
{\r
- _shipMaster = master;\r
_shipInfo = ship;\r
_itemInfo = item;\r
+ _battleInfo = battle;\r
_writer = new LogWriter().Write;\r
_nowFunc = () => DateTime.Now;\r
}\r
_nowFunc = nowFunc;\r
}\r
\r
+ public void FlashLog()\r
+ {\r
+ FlashAchivementLog();\r
+ }\r
+\r
public void InspectMissionResult(dynamic json)\r
{\r
var r = (int)json.api_clear_result;\r
if (flag == 1)\r
material[(int)Material.Bucket] = count;\r
else if (flag == 2)\r
- material[(int)Material.Burner] = count;\r
+ material[(int)Material.Burner + 2] = count; // 高速建造材と開発資材が反対なのでいつか直す\r
else if (flag == 3)\r
- material[(int)Material.Development] = count;\r
+ material[(int)Material.Development - 2] = count;\r
}\r
if ((_logType & LogType.Mission) != 0)\r
{\r
}\r
}\r
\r
- public void InspectMap(dynamic json)\r
+ public void InspectMapStart(dynamic json)\r
{\r
+ _start = true;\r
_map = json;\r
+ _battle = null;\r
+ if ((_logType & LogType.Material) != 0)\r
+ WriteMaterialLog(_nowFunc());\r
}\r
\r
- public void InspectBattle(dynamic json)\r
+ public void InspectMapNext(dynamic json)\r
+ {\r
+ if ((_logType & LogType.Achivement) != 0 && json.api_get_eo_rate() && (int)json.api_get_eo_rate != 0)\r
+ {\r
+ _writer("戦果",\r
+ _nowFunc().ToString(DateTimeFormat) + "," + _lastExp + "," + (int)json.api_get_eo_rate,\r
+ "日付,経験値,EO");\r
+ }\r
+ _map = json;\r
+ }\r
+\r
+ public void InspectClearItemGet(dynamic json)\r
{\r
- if (!IsNightBattle(json))\r
- _battle = json;\r
+ if ((_logType & LogType.Achivement) == 0)\r
+ return;\r
+ if (json.api_bounus_count == 0)\r
+ return;\r
+ foreach (var entry in json.api_bounus)\r
+ {\r
+ if (entry.api_type != 18)\r
+ continue;\r
+ _writer("戦果",\r
+ _nowFunc().ToString(DateTimeFormat) + "," + _lastExp + "," + (int)entry.api_count,\r
+ "日付,経験値,EO");\r
+ break;\r
+ }\r
+\r
}\r
\r
- private bool IsNightBattle(dynamic json)\r
+ public void InspectBattle(dynamic json)\r
{\r
- return json.api_hougeki();\r
+ if (_battle != null) // 通常の夜戦は無視する\r
+ return;\r
+ _battle = json;\r
}\r
\r
public void InspectBattleResult(dynamic result)\r
{\r
+ if ((_logType & LogType.Achivement) != 0 && result.api_get_exmap_rate())\r
+ {\r
+ var rate = result.api_get_exmap_rate is string\r
+ ? int.Parse(result.api_get_exmap_rate)\r
+ : (int)result.api_get_exmap_rate;\r
+ if (rate != 0)\r
+ {\r
+ _writer("戦果", _nowFunc().ToString(DateTimeFormat) + "," + _lastExp + "," + rate,\r
+ "日付,経験値,EO");\r
+ }\r
+ }\r
if ((_logType & LogType.Battle) == 0 || _map == null || _battle == null)\r
{\r
_map = _battle = null;\r
return;\r
}\r
- var fships = new List<string>();\r
- var deck = _shipInfo.GetDeck(_battle.api_dock_id() ? (int)_battle.api_dock_id - 1 : 0);\r
- fships.AddRange(deck.Select(id =>\r
+ var fships = GenerateFirendShipList();\r
+ var eships = GenerateEnemyShipList();\r
+ var cell = (int)_map.api_no;\r
+ var boss = "";\r
+ if (_start)\r
+ boss = "出撃";\r
+ if (cell == (int)_map.api_bosscell_no || (int)_map.api_event_id == 5)\r
+ boss = _start ? "出撃&ボス" : "ボス";\r
+ var dropType = result.api_get_ship() ? result.api_get_ship.api_ship_type : "";\r
+ if (result.api_get_useitem())\r
{\r
- if (id == -1)\r
- return ",";\r
- var s = _shipInfo[id];\r
- return string.Format("{0}(Lv{1}),{2}/{3}", s.Name, s.Level, s.NowHp, s.MaxHp);\r
- }));\r
- var edeck = ((int[])_battle.api_ship_ke).Skip(1).ToArray();\r
- var enowhp = ((int[])_battle.api_nowhps).Skip(7).ToArray();\r
- var emaxhp = ((int[])_battle.api_maxhps).Skip(7).ToArray();\r
- var eships = new List<string>();\r
- for (var i = 0; i < edeck.Count(); i++)\r
+ if (dropType == "")\r
+ dropType = "アイテム";\r
+ else\r
+ dropType += "+アイテム";\r
+ }\r
+ var dropName = result.api_get_ship() ? result.api_get_ship.api_ship_name : "";\r
+ if (result.api_get_useitem())\r
{\r
- eships.Add(edeck[i] == -1\r
- ? ","\r
- : string.Format("{0},{1}/{2}", _shipMaster[edeck[i]].Name, enowhp[i], emaxhp[i]));\r
+ var itemName = _itemInfo.GetUseItemName((int)result.api_get_useitem.api_useitem_id);\r
+ if (dropName == "")\r
+ dropName = itemName;\r
+ else\r
+ dropName += "+" + itemName;\r
}\r
- var cell = (int)_map.api_no;\r
- var boss = cell == (int)_map.api_bosscell_no || (int)_map.api_event_id == 5 ? "ボス" : "";\r
+ var fp = _shipInfo.GetFighterPower(BattleInfo.DeckId(_battle));\r
+ var fpower = fp[0] == fp[1] ? fp[0].ToString() : fp[0] + "~" + fp[1];\r
_writer("海戦・ドロップ報告書", string.Join(",", _nowFunc().ToString(DateTimeFormat),\r
result.api_quest_name,\r
cell, boss,\r
FormationName(_battle.api_formation[0]),\r
FormationName(_battle.api_formation[1]),\r
result.api_enemy_info.api_deck_name,\r
- result.api_get_ship() ? result.api_get_ship.api_ship_type : "",\r
- result.api_get_ship() ? result.api_get_ship.api_ship_name : "",\r
+ dropType, dropName,\r
string.Join(",", fships),\r
- string.Join(",", eships)),\r
+ string.Join(",", eships),\r
+ fpower, _battleInfo.EnemyFighterPower.AirCombat + _battleInfo.EnemyFighterPower.UnknownMark,\r
+ AirControlLevelName(_battle)),\r
"日付,海域,マス,ボス,ランク,艦隊行動,味方陣形,敵陣形,敵艦隊,ドロップ艦種,ドロップ艦娘," +\r
"味方艦1,味方艦1HP,味方艦2,味方艦2HP,味方艦3,味方艦3HP,味方艦4,味方艦4HP,味方艦5,味方艦5HP,味方艦6,味方艦6HP," +\r
- "敵艦1,敵艦1HP,敵艦2,敵艦2HP,敵艦3,敵艦3HP,敵艦4,敵艦4HP,敵艦5,敵艦5HP,敵艦6,敵艦6HP"\r
+ "敵艦1,敵艦1HP,敵艦2,敵艦2HP,敵艦3,敵艦3HP,敵艦4,敵艦4HP,敵艦5,敵艦5HP,敵艦6,敵艦6HP," +\r
+ "味方制空値,敵制空値,制空状態"\r
);\r
_map = _battle = null;\r
+ _start = false;\r
+ }\r
+\r
+ private IEnumerable<string> GenerateFirendShipList()\r
+ {\r
+ int deckId = BattleInfo.DeckId(_battle);\r
+ if (_battle.api_nowhps_combined() && (int)_battle.api_nowhps_combined[1] != -1)\r
+ {\r
+ var main = _shipInfo.GetDeck(0);\r
+ var guard = _shipInfo.GetDeck(1);\r
+ return main.Zip(guard, (m, g) =>\r
+ {\r
+ if (m == -1 && g == -1)\r
+ return ",";\r
+ var name = "";\r
+ var hp = "";\r
+ if (m != -1)\r
+ {\r
+ var sm = _shipInfo.GetStatus(m);\r
+ name = $"{sm.Name}(Lv{sm.Level})";\r
+ hp = $"{sm.NowHp}/{sm.MaxHp}";\r
+ }\r
+ name += "・";\r
+ hp += "・";\r
+ if (g != -1)\r
+ {\r
+ var sg = _shipInfo.GetStatus(g);\r
+ name += $"{sg.Name}(Lv{sg.Level})";\r
+ hp += $"{sg.NowHp}/{sg.MaxHp}";\r
+ }\r
+ return name + "," + hp;\r
+ }).ToList();\r
+ }\r
+ var deck = _shipInfo.GetDeck(deckId);\r
+ return deck.Select(id =>\r
+ {\r
+ if (id == -1)\r
+ return ",";\r
+ var s = _shipInfo.GetStatus(id);\r
+ return $"{s.Name}(Lv{s.Level}),{s.NowHp}/{s.MaxHp}";\r
+ }).ToList();\r
+ }\r
+\r
+ private IEnumerable<string> GenerateEnemyShipList()\r
+ {\r
+ var result = _battleInfo.EnemyResultStatus;\r
+ if (result.Length <= 6)\r
+ return result.Select(s => s.Id == -1 ? "," : $"{s.Name},{s.NowHp}/{s.MaxHp}").ToList();\r
+ var main = result.Take(6);\r
+ var guard = result.Skip(6);\r
+ return main.Zip(guard, (m, g) =>\r
+ {\r
+ if (m.Id == -1 && g.Id == -1)\r
+ return ",";\r
+ var name = "";\r
+ var hp = "";\r
+ if (m.Id != -1)\r
+ {\r
+ name = $"{m.Name}";\r
+ hp = $"{m.NowHp}/{m.MaxHp}";\r
+ }\r
+ name += "・";\r
+ hp += "・";\r
+ if (g.Id != -1)\r
+ {\r
+ name += $"{g.Name}";\r
+ hp += $"{g.NowHp}/{g.MaxHp}";\r
+ }\r
+ return name + "," + hp;\r
+ }).ToList();\r
}\r
\r
private string FormationName(dynamic f)\r
}\r
}\r
\r
- private static String BattleFormationName(int f)\r
+ private static string BattleFormationName(int f)\r
{\r
switch (f)\r
{\r
}\r
}\r
\r
+ private string AirControlLevelName(dynamic json)\r
+ {\r
+ // BattleInfo.AirControlLevelは夜戦で消されているかもしれないので、こちらで改めて調べる。\r
+ if (!json.api_kouku())\r
+ return "";\r
+ var stage1 = json.api_kouku.api_stage1;\r
+ if (stage1 == null)\r
+ return "";\r
+ if (stage1.api_f_count == 0 && stage1.api_e_count == 0)\r
+ return "";\r
+ switch ((int)stage1.api_disp_seiku)\r
+ {\r
+ case 0:\r
+ return "航空均衡";\r
+ case 1:\r
+ return "制空権確保";\r
+ case 2:\r
+ return "航空優勢";\r
+ case 3:\r
+ return "航空劣勢";\r
+ case 4:\r
+ return "制空権喪失";\r
+ default:\r
+ return "";\r
+ }\r
+ }\r
+\r
public void InspectBasic(dynamic json)\r
{\r
_basic = json;\r
+ if ((_logType & LogType.Achivement) == 0)\r
+ return;\r
+ var now = _nowFunc();\r
+ var exp = (int)json.api_experience;\r
+ var isNewMonth = _endOfMonth == DateTime.MinValue || now.CompareTo(_endOfMonth) >= 0;\r
+ var isNewDate = _nextDate == DateTime.MinValue || now.CompareTo(_nextDate) >= 0;\r
+ if (isNewDate || isNewMonth)\r
+ {\r
+ if (_lastDate != DateTime.MinValue)\r
+ {\r
+ _writer("戦果", _lastDate.ToString(DateTimeFormat) + "," + _lastExp + ",0", "日付,経験値,EO");\r
+ }\r
+ _writer("戦果", now.ToString(DateTimeFormat) + "," + exp + ",0", "日付,経験値,EO");\r
+ if (isNewMonth)\r
+ {\r
+ _endOfMonth = new DateTime(now.Year, now.Month, DateTime.DaysInMonth(now.Year, now.Month), 22, 0, 0);\r
+ if (_endOfMonth.CompareTo(now) <= 0)\r
+ {\r
+ var days = _endOfMonth.Month == 12\r
+ ? DateTime.DaysInMonth(_endOfMonth.Year + 1, 1)\r
+ : DateTime.DaysInMonth(_endOfMonth.Year, _endOfMonth.Month);\r
+ _endOfMonth = _endOfMonth.AddDays(days);\r
+ }\r
+ }\r
+ _nextDate = new DateTime(now.Year, now.Month, now.Day, 2, 0, 0);\r
+ if (now.Hour >= 2)\r
+ _nextDate = _nextDate.AddDays(1);\r
+ if (_nextDate.Day == 1)\r
+ _nextDate = _nextDate.AddDays(1);\r
+ }\r
+ _lastDate = now;\r
+ _lastExp = exp;\r
+ }\r
+\r
+ private void FlashAchivementLog()\r
+ {\r
+ if ((_logType & LogType.Achivement) == 0)\r
+ return;\r
+ if (_lastDate != DateTime.MinValue)\r
+ {\r
+ _writer("戦果", _lastDate.ToString(DateTimeFormat) + "," + _lastExp + ",0", "日付,経験値,EO");\r
+ }\r
}\r
\r
public void InspectCreateItem(string request, dynamic json)\r
return;\r
var kdock = ((dynamic[])json).First(e => e.api_id == _kdockId);\r
var material = Enumerable.Range(1, 5).Select(i => (int)kdock["api_item" + i]).ToArray();\r
- var ship = _shipMaster[(int)kdock.api_created_ship_id];\r
+ var ship = _shipInfo.GetSpec((int)kdock.api_created_ship_id);\r
var avail = ((dynamic[])json).Count(e => (int)e.api_state == 0);\r
_writer("建造報告書",\r
_nowFunc().ToString(DateTimeFormat) + "," +\r
{\r
if ((_logType & LogType.Material) == 0)\r
return;\r
+ foreach (var e in json)\r
+ _currentMaterial[(int)e.api_id - 1] = (int)e.api_value;\r
var now = _nowFunc();\r
- if (now - _prevTime < TimeSpan.FromMinutes(10))\r
+ if (now - _prevTime < TimeSpan.FromMinutes(_materialLogInterval))\r
return;\r
+ WriteMaterialLog(now);\r
+ }\r
+\r
+ public void WriteMaterialLog(DateTime now)\r
+ {\r
_prevTime = now;\r
- var material = new int[8];\r
- foreach (var e in json)\r
- material[(int)e.api_id - 1] = (int)e.api_value;\r
_writer("資材ログ",\r
now.ToString(DateTimeFormat) + "," +\r
- string.Join(",", material) + ",",\r
- "日付,燃料,弾薬,鋼材,ボーキ,高速修復材,高速建造材,開発資材,改修資材");\r
+ string.Join(",", _currentMaterial),\r
+ "日付,燃料,弾薬,鋼材,ボーキ,高速建造材,高速修復材,開発資材,改修資材");\r
+ }\r
+\r
+ public void SetCurrentMaterial(int[] material)\r
+ {\r
+ _currentMaterial = material;\r
+ }\r
+\r
+ public void InspectRemodelSlot(string request, dynamic json)\r
+ {\r
+ if ((_logType & LogType.RemodelSlot) == 0)\r
+ return;\r
+ var now = _nowFunc();\r
+ var values = HttpUtility.ParseQueryString(request);\r
+ var id = int.Parse(values["api_slot_id"]);\r
+ var name = _itemInfo.GetName(id);\r
+ var level = _itemInfo.GetStatus(id).Level;\r
+ var success = (int)json.api_remodel_flag == 1 ? "○" : "×";\r
+ var certain = int.Parse(values["api_certain_flag"]) == 1 ? "○" : "";\r
+ var useName = "";\r
+ var useNum = "";\r
+ if (json.api_use_slot_id())\r
+ {\r
+ var use = (int[])json.api_use_slot_id;\r
+ useName = _itemInfo.GetName(use[0]);\r
+ useNum = use.Length.ToString();\r
+ }\r
+ var after = (int[])json.api_after_material;\r
+ var diff = new int[after.Length];\r
+ for (var i = 0; i < after.Length; i++)\r
+ diff[i] = _currentMaterial[i] - after[i];\r
+ var ship1 = Secretary();\r
+ var ship2 = "";\r
+ var ships = _shipInfo.GetShipStatuses(0);\r
+ if (ships.Length >= 2)\r
+ ship2 = ships[1].Name + "(" + ships[1].Level + ")";\r
+ _writer("改修報告書",\r
+ now.ToString(DateTimeFormat) + "," +\r
+ string.Join(",", name, level, success, certain, useName, useNum,\r
+ diff[(int)Material.Fuel], diff[(int)Material.Bullet], diff[(int)Material.Steal],\r
+ diff[(int)Material.Bouxite],\r
+ diff[(int)Material.Development], diff[(int)Material.Screw],\r
+ ship1, ship2),\r
+ "日付,改修装備,レベル,成功,確実化,消費装備,消費数,燃料,弾薬,鋼材,ボーキ,開発資材,改修資材,秘書艦,二番艦");\r
}\r
}\r
\r
public class LogWriter\r
{\r
private readonly IFile _file;\r
+ private readonly string _outputDir;\r
\r
public interface IFile\r
{\r
// Shift_JISでないとExcelで文字化けする\r
private readonly Encoding _encoding = Encoding.GetEncoding("Shift_JIS");\r
\r
- public string ReadAllText(string path)\r
- {\r
- return File.ReadAllText(path, _encoding);\r
- }\r
+ public string ReadAllText(string path) => File.ReadAllText(path, _encoding);\r
\r
public void AppendAllText(string path, string text)\r
{\r
File.Delete(path);\r
}\r
\r
- public bool Exists(string path)\r
- {\r
- return File.Exists(path);\r
- }\r
+ public bool Exists(string path) => File.Exists(path);\r
}\r
\r
- public LogWriter(IFile file = null)\r
+ public LogWriter(string outputDir = null, IFile file = null)\r
{\r
+ _outputDir = outputDir ?? AppDomain.CurrentDomain.BaseDirectory;\r
_file = file ?? new FileWrapper();\r
}\r
\r
public void Write(string file, string s, string header)\r
{\r
- // ReSharper disable once AssignNullToNotNullAttribute\r
- var path = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), file);\r
+ var path = Path.Combine(_outputDir, file);\r
var csv = path + ".csv";\r
var tmp = path + ".tmp";\r
if (_file.Exists(tmp))\r
_file.AppendAllText(f, s + "\r\n");\r
break;\r
}\r
- catch (IOException)\r
+ catch (IOException e)\r
{\r
+ if (f == tmp)\r
+ throw new LogIOException("報告書の出力中にエラーが発生しました。", e);\r
}\r
}\r
}\r
}\r
+\r
+ public class LogIOException : Exception\r
+ {\r
+ public LogIOException()\r
+ {\r
+ }\r
+\r
+ public LogIOException(string message) : base(message)\r
+ {\r
+ }\r
+\r
+ public LogIOException(string message, Exception inner) : base(message, inner)\r
+ {\r
+ }\r
+ }\r
}
\ No newline at end of file