/*
* プロジェクト参照
* ・アセンブリ/フレームワーク
* ・System.Web
* ・参照
* ・DynamicJson.dll
* ・FiddlerCore4.dll
*/
using Codeplex.Data;
using Fiddler;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
namespace kcsapiLog
{
class Program
{
static readonly string[] formation = { null, "単縦陣", "複縦陣", "輪形陣", "梯形陣", "単横陣" };
static readonly string[] intercept = { null, "同航戦", "反航戦", "T字戦(有利)", "T字戦(不利)" };
static dynamic api_deck; // 艦隊
static Dictionary<int, dynamic> mst_ship = new Dictionary<int, dynamic>(); // 艦マスタ
static Dictionary<int, dynamic> mst_slot = new Dictionary<int, dynamic>(); // 装備マスタ
static Dictionary<int, dynamic> lst_ship = new Dictionary<int, dynamic>(); // 保有艦リスト
static Dictionary<int, int> lst_slot = new Dictionary<int, int>(); // 装備保有リスト
static Dictionary<string, string> map_deck = new Dictionary<string, string>();
static string map_id;
static void Main(string[] args)
{
ParseApiStart2();
ParseApiPort();
FiddlerApplication.AfterSessionComplete += FiddlerApplication_AfterSessionComplete;
Console.CancelKeyPress += Console_CancelKeyPress;
Disp("Starting {0}...", FiddlerApplication.GetVersionString());
FiddlerApplication.Startup(0, true, true);
Disp("Hit CTRL+C to end session.");
try
{
bool bDone = false;
do
{
ConsoleKeyInfo cki = Console.ReadKey();
Console.WriteLine();
switch (cki.KeyChar)
{
case '1': DispDeck(1); break;
case '2': DispDeck(2); break;
case '3': DispDeck(3); break;
case '4': DispDeck(4); break;
case 'n': DispNDock(); break;
case 'q':
bDone = true;
break;
}
} while (!bDone);
}
catch (Exception e)
{
Disp(e.ToString());
}
DoQuit();
}
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
DoQuit();
}
private static void DoQuit()
{
DispColor(ConsoleColor.Yellow, "Shutting down...");
FiddlerApplication.Shutdown();
Thread.Sleep(500);
}
private static void Disp(string format, params object[] arg)
{
DispColor(ConsoleColor.Gray, format, arg);
}
private static void DispColor(ConsoleColor color, string format, params object[] arg)
{
Console.ForegroundColor = color;
Console.WriteLine(format, arg);
Console.ResetColor();
}
static void FiddlerApplication_AfterSessionComplete(Session oSession)
{
string[] url = oSession.fullUrl.Split('/');
if (5 <= url.Length && url[3] == "kcsapi")
{
if (oSession.oResponse.MIMEType == "text/plain")
{
string req = oSession.GetRequestBodyAsString();
string res = oSession.GetResponseBodyAsString();
string dir = string.Join("/", url, 4, url.Length - 4);
DispColor(ConsoleColor.DarkGreen, dir);
string fname = "";
switch (dir)
{
case "api_start2":
fname = "api_start2";
break;
case "api_port/port":
fname = "api_port";
break;
case "api_get_member/slot_item":
fname = "slot_item";
break;
}
if (fname != "")
{
using (StreamWriter sw = new StreamWriter(fname + ".js"))
{
sw.Write(res);
}
}
using (StreamWriter sw = new StreamWriter("Response.log", true)) // append
{
sw.WriteLine(oSession.fullUrl + "\t" + req + "\t" + res);
}
NameValueCollection nvc = HttpUtility.ParseQueryString(req);
dynamic json = svdata2json(res);
dynamic data = json.api_data() ? json.api_data : null;
switch (url[4])
{
case "api_get_member":
switch (url[5])
{
case "mapinfo":
GetMember_Mapinfo(data);
break;
case "mapcell":
Disp("出撃開始");
break;
case "ndock":
Disp("入渠");
break;
case "practice":
GetMember_Practice(data);
break;
case "ship2":
api_deck = json.api_data_deck;
CreateLstShip(data);
break;
case "slot_item":
GetMember_SlotItem(data);
break;
}
break;
case "api_port":
switch (url[5])
{
case "port":
ParseApiPort(json);
break;
}
break;
case "api_req_battle_midnight":
switch (url[5])
{
case "battle":
Disp("夜戦");
break;
case "sp_midnight":
ReqBattleMidnight_SpMidnight(data);
break;
}
break;
case "api_req_hokyu":
switch (url[5])
{
case "charge":
ReqHokyu_Charge(data);
break;
}
break;
case "api_req_kousyou":
switch (url[5])
{
case "createitem":
ReqKousyou_Createitem(nvc, data);
break;
}
break;
case "api_req_map":
switch (url[5])
{
case "next":
ReqMap(data);
break;
case "start":
ReqMap(data);
break;
}
break;
case "api_req_member":
switch (url[5])
{
case "get_practice_enemyinfo":
ReqMember_GetPracticeEnemyinfo(data);
break;
}
break;
case "api_req_practice":
switch (url[5])
{
case "battle":
ReqPractice_Battle(data);
break;
case "battle_result":
ReqPractice_BattleResult(data);
break;
case "midnight_battle":
ReqPractice_MidnightBattle(data);
break;
}
break;
case "api_req_sortie":
switch (url[5])
{
case "battle":
ReqSortie_Battle(data);
break;
case "battleresult":
ReqSortie_Battleresult(data);
break;
}
break;
}
}
}
}
// 各種マスタ
private static void ParseApiStart2()
{
dynamic json = ReadJson("api_start2.js");
dynamic data = json.api_data;
foreach (dynamic ship in data.api_mst_ship)
{
mst_ship[(int)ship.api_id] = ship;
}
foreach (dynamic slot in data.api_mst_slotitem)
{
mst_slot[(int)slot.api_id] = slot;
}
}
// 母港情報
private static void ParseApiPort(dynamic json = null)
{
if (json == null)
{
json = ReadJson("api_port.js");
}
dynamic data = json.api_data;
api_deck = data.api_deck_port;
CreateLstShip(data.api_ship);
}
// JSONファイル読み込み
private static dynamic ReadJson(string path)
{
string svdata;
using (StreamReader sr = new StreamReader(path))
{
svdata = sr.ReadToEnd();
}
return svdata2json(svdata);
}
private static dynamic svdata2json(string svdata)
{
string res = Regex.Replace(svdata, @"\\u(?<code>[0-9A-Fa-f]{4})",
(Match m) => ((char)Convert.ToInt32(m.Groups["code"].Value, 16)).ToString());
return DynamicJson.Parse(res.Substring(7));
}
//==============================================================================
private static void GetMember_Mapinfo(dynamic data)
{
Disp("出撃");
foreach (dynamic map in data)
{
if (map.api_eventmap() && (int)map.api_cleared == 0)
{
int max_maphp = (int)map.api_eventmap.api_max_maphp;
int now_maphp = (int)map.api_eventmap.api_now_maphp;
Disp("map:{0} {1}/{2} {3}%", (int)map.api_id,
now_maphp, max_maphp, 100 * now_maphp / max_maphp);
}
}
}
private static void GetMember_Practice(dynamic data)
{
Disp("演習");
foreach (dynamic d in data)
{
Disp("{0}「{1}」", d.api_enemy_name, d.api_enemy_comment);
}
}
// 装備アイテム
private static void GetMember_SlotItem(dynamic data)
{
lst_slot.Clear();
foreach (dynamic d in data)
{
lst_slot[(int)d.api_id] = (int)d.api_slotitem_id;
}
Disp("装備アイテム保有数 {0}", lst_slot.Count);
}
private static void ReqBattleMidnight_SpMidnight(dynamic data)
{
MidnightBattle(data);
}
private static void ReqHokyu_Charge(dynamic data)
{
Disp("補給");
Disp("ボーキサイト消費:{0}", (int)data.api_use_bou);
}
private static void ReqKousyou_Createitem(NameValueCollection nvc, dynamic data)
{
Disp("開発開始");
string str = "失敗";
if (data.api_slot_item())
{
str = mst_slot[(int)data.api_slot_item.api_slotitem_id].api_name;
}
var mat = new string[4];
for (var i = 0; i < mat.Length; i++)
{
mat[i] = nvc["api_item" + (i + 1)];
}
Disp("{0} {1}", string.Join("/", mat), str);
}
private static void ReqMap(dynamic data)
{
map_id = string.Format("{0}-{1}-{2}",
(int)data.api_maparea_id, (int)data.api_mapinfo_no, (int)data.api_no);
Disp("{0} 羅針盤:{1}", map_id, data.api_rashin_flg);
if (data.api_itemget())
{
dynamic item = data.api_itemget;
Disp("item:{0} {1}", (int)item.api_id, (int)item.api_getcount);
}
}
private static void ReqMember_GetPracticeEnemyinfo(dynamic data)
{
Disp("艦隊名:{0}", data.api_deckname);
foreach (dynamic ship in data.api_deck.api_ships)
{
int id = (int)ship.api_ship_id;
if (id < 0) break;
Disp("lv{0} {1}", (int)ship.api_level, GetShipName(id));
}
}
private static void ReqPractice_Battle(dynamic data)
{
Battle(data);
}
private static void ReqPractice_BattleResult(dynamic data)
{
BattleResult(data);
}
private static void ReqPractice_MidnightBattle(dynamic data)
{
MidnightBattle(data);
}
private static void ReqSortie_Battle(dynamic data)
{
Battle(data);
}
private static void ReqSortie_Battleresult(dynamic data)
{
BattleResult(data);
Disp("艦保有数 {0} 隻", lst_ship.Count);
string ship_type = "";
string ship_name = "";
if (data.api_get_ship())
{
dynamic ship = data.api_get_ship;
ship_type = ship.api_ship_type;
ship_name = ship.api_ship_name;
}
string line = string.Join(",",
DateTime.Now.ToString(), map_id, data.api_win_rank, ship_type, ship_name);
Disp(line);
using (StreamWriter sw = new StreamWriter("get_ship.csv", true, Encoding.Default))
{
sw.WriteLine(line);
}
}
private static void Battle(dynamic data)
{
int[] ship_ke = data.api_ship_ke;
for (int i = 1; i < ship_ke.Length; i++)
{
int id = ship_ke[i];
if (id < 0) continue;
dynamic mship = mst_ship[id];
Disp("{0} lv{1} hp{2} {3} {4}", i, (int)data.api_ship_lv[i], (int)data.api_maxhps[6 + i],
mship.api_name, mship.api_yomi);
}
dynamic form = data.api_formation;
Disp("自:{0} 敵:{1} {2}",
formation[(int)form[0]], formation[(int)form[1]], intercept[(int)form[2]]);
Disp("艦隊:{0} 夜戦:{1}", (int)data.api_dock_id, (int)data.api_midnight_flag);
/*
api_kouku 航空
api_stage1
api_disp_seiku
api_f_count 味方制空値
api_f_lostcount
api_stage3
api_edam 敵ダメージ
api_opening_atack 開幕雷撃
api_edam 敵ダメージ
api_frai 味方攻撃対象
*/
}
private static void MidnightBattle(dynamic data)
{
dynamic hougeki = data.api_hougeki;
for (int i = 1; i < ((int[])hougeki.api_at_list).Length; i++)
{
Disp("at{0} sp{1}",
(int)hougeki.api_at_list[i], (int)hougeki.api_sp_list[i]);
}
// at_list 攻撃番号配列
// cl_list クリティカル
// damage ダメージ
// df_list 防御番号
// si_list 攻撃装備id slot item
// sp_list 特殊攻撃 1:連撃 3:カットイン雷撃
}
private static void BattleResult(dynamic data)
{
Disp("敵艦隊名:{0}", data.api_enemy_info.api_deck_name);
Disp("win_rank:{0} mvp:{1}", data.api_win_rank, (int)data.api_mvp);
Disp("基本経験値:{0} 提督経験値:{1}", (int)data.api_get_base_exp, (int)data.api_get_exp);
// api_dests 撃沈数
// api_destsf 旗艦撃沈
}
//==============================================================================
// 保有艦リスト
private static void CreateLstShip(dynamic api_ship)
{
Disp("保有艦リスト更新 {0}", DateTime.Now);
lst_ship.Clear();
foreach (dynamic ship in api_ship)
{
lst_ship[(int)ship.api_id] = ship;
}
Disp("艦保有数 {0} 隻", lst_ship.Count);
DispDeck(1);
}
// 艦名の取得
private static string GetShipName(int ship_id)
{
dynamic mship = mst_ship[ship_id];
return mship.api_name;
}
// 艦隊の表示
private static void DispDeck(int id)
{
foreach (dynamic i in api_deck[id - 1].api_ship)
{
int ship_uid = (int)i;
if (ship_uid < 0) break;
dynamic ship = lst_ship[ship_uid];
dynamic mship = mst_ship[(int)ship.api_ship_id];
Disp("lv{0}.{1:D2} co{2} 燃{4}/弾{5} hp{3}% {6}",
(int)ship.api_lv, (int)ship.api_exp[2], (int)ship.api_cond,
100 * (int)ship.api_nowhp / (int)ship.api_maxhp,
Math.Round(10 * ship.api_fuel / mship.api_fuel_max),
Math.Round(10 * ship.api_bull / mship.api_bull_max),
mship.api_name);
}
}
// 入渠ドックの表示
private static void DispNDock()
{
var list = new List<Tuple<int, string>>();
foreach (var kvp in lst_ship)
{
dynamic ship = kvp.Value;
int hp = (int)ship.api_maxhp - (int)ship.api_nowhp;
if (0 < hp)
{
int sec = (int)ship.api_ndock_time / 1000;
int nowhp = (int)ship.api_nowhp;
int maxhp = (int)ship.api_maxhp;
TimeSpan ts = new TimeSpan(0, 0, sec);
list.Add(Tuple.Create(sec, string.Format("{0} {1} {2}/{3} {4}", ts.ToString("g"),
maxhp - nowhp, nowhp, maxhp, GetShipName((int)ship.api_ship_id))));
}
}
list.Sort((Tuple<int, string> t1, Tuple<int, string> t2) => t2.Item1 - t1.Item1);
foreach (var t in list)
{
Disp(t.Item2);
}
}
}
}