├── .gitattributes
├── .gitignore
├── DebugPlatform
├── App.config
├── DebugPlatform.csproj
├── GraphList.cs
├── Last-Modified - 副本.xml
├── Last-Modified.xml
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── Settings.cs
├── KanColleCacher.sln
├── KanColleCacher
├── CacheCore.cs
├── CacherToolView.xaml
├── CacherToolView.xaml.cs
├── ConfigParser.cs
├── Extension.cs
├── FiddlerRules.cs
├── GMTHelper.cs
├── GraphList.cs
├── KanColleCacher.cs
├── KanColleCacher.csproj
├── Libraries
│ ├── FiddlerCore4.dll
│ ├── FiddlerCore4.xml
│ ├── KanColleViewer.exe
│ ├── KanColleWrapper.dll
│ ├── Livet.Extensions.dll
│ ├── Livet.Extensions.xml
│ ├── Livet.dll
│ └── Portable.dll
├── Log.cs
├── Properties
│ └── AssemblyInfo.cs
├── RecentRecord.cs
├── Settings - 副本.cs
├── Settings.cs
├── ValidationRule.cs
├── ValueConverter.cs
└── VersionChecker.cs
├── KanColleModifier.KCV - 副本
├── KanColleModifier.KCV.csproj
├── Libraries
│ ├── FiddlerCore4.dll
│ └── KanColleViewer.exe
├── Modifier.cs
├── ModifierInitializer.cs
├── ModifierView.xaml
├── ModifierView.xaml.cs
└── Properties
│ └── AssemblyInfo.cs
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/.gitignore
--------------------------------------------------------------------------------
/DebugPlatform/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DebugPlatform/DebugPlatform.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D8F14FD8-B541-40B0-9D52-32FF0A7EB6B3}
8 | Exe
9 | Properties
10 | DebugPlatform
11 | DebugPlatform
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | ..\KanColleCacher\Libraries\FiddlerCore4.dll
37 |
38 |
39 | ..\KanColleCacher\Libraries\KanColleWrapper.dll
40 |
41 |
42 | ..\KanColleCacher\Libraries\Livet.dll
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | ConfigParser.cs
61 |
62 |
63 | GMTHelper.cs
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
81 |
--------------------------------------------------------------------------------
/DebugPlatform/GraphList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Grabacr07.KanColleWrapper;
7 | using Grabacr07.KanColleWrapper.Models;
8 | using Grabacr07.KanColleWrapper.Models.Raw;
9 | using Fiddler;
10 | using System.IO;
11 | using Debug = System.Diagnostics.Debug;
12 | //using System.Runtime.Serialization.Json;
13 | using System.Windows;
14 |
15 |
16 | namespace d_f_32.KanColleCacher
17 | {
18 | class GraphList
19 | {
20 |
21 | public static void DebugFunc()
22 | {
23 | GenerateList();
24 | }
25 |
26 | static List graphList = new List();
27 |
28 | ///
29 | /// 将解析完成的信息保存到本地
30 | ///
31 | static void PrintToFile()
32 | {
33 | //string filepath = Settings.Current.CacheFolder + "\\GraphList.txt";
34 | string filepath = @"E:\Game\KanColleViewer ver.3.3\MyCache\GraphList.txt";
35 | StringBuilder content = new StringBuilder();
36 |
37 | content.AppendFormat(
38 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n",
39 | "SortNo", "ShipId", "ShipName",
40 | "FileName", "FileVersion",
41 | "TypeName", "TypeId"
42 | );
43 | content.AppendFormat(
44 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n",
45 | "序号", "ID", "名称",
46 | "文件名", "文件版本", "文件序号",
47 | "类型", "类型序号", "类型ID"
48 | );
49 | try
50 | {
51 | graphList.Sort((x, y) =>
52 | {
53 | if (x.ship_sortno == y.ship_sortno)
54 | {
55 | if (x.ship_id == y.ship_id)
56 | return 0;
57 |
58 | return x.ship_id < y.ship_id ? -1 : 1;
59 | }
60 |
61 | return x.ship_sortno < y.ship_sortno ? -1 : 1;
62 | });
63 | }
64 | catch (Exception ex)
65 | {
66 | Debug.WriteLine("Cachr> GraphList.PrintToFile() 排序时发生异常(graphList.Sort)");
67 | Debug.WriteLine(ex);
68 | }
69 |
70 |
71 | graphList.ForEach(x =>
72 | {
73 | content.AppendFormat(
74 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n",
75 | x.ship_sortno, x.ship_id, x.ship_name,
76 | x.ship_filename, x.ship_version,
77 | x.ship_type_name, x.ship_type_id
78 | );
79 | });
80 |
81 | try
82 | {
83 | File.WriteAllText(filepath, content.ToString());
84 | }
85 | catch (Exception ex)
86 | {
87 | //Log.Exception(ex.Source, ex, "写入立绘列表文件时异常");
88 | }
89 | }
90 |
91 | ///
92 | /// 解析 api_start2 数据信息
93 | ///
94 | static void ParseSession(Session oSession)
95 | {
96 | SvData svd;
97 | if (!SvData.TryParse(oSession, out svd))
98 | {
99 | //Log.Warning("GraphList.ParseSession()", "TryParse失败,无效的Session对象!");
100 | return;
101 | }
102 |
103 | var mst_shipgraph = svd.Data.api_mst_shipgraph
104 | .ToDictionary(x => x.api_id);
105 | var mst_ship = svd.Data.api_mst_ship
106 | .ToDictionary(x => x.api_id);
107 | var mst_stype = svd.Data.api_mst_stype
108 | .ToDictionary(x => x.api_id);
109 |
110 | graphList.Clear();
111 |
112 | foreach (var _pair in mst_shipgraph)
113 | {
114 | var item = new ship_graph_item();
115 | var _loc1 = _pair.Value;
116 |
117 | item.ship_id = _loc1.api_id;
118 | item.ship_filename = _loc1.api_filename;
119 | item.ship_version = _loc1.api_version;
120 | item.ship_graph_sortno = _loc1.api_sortno;
121 |
122 | if (mst_ship.ContainsKey(item.ship_id))
123 | {
124 | var _loc2 = mst_ship[item.ship_id];
125 |
126 | item.ship_sortno = _loc2.api_sortno;
127 | item.ship_name = _loc2.api_name;
128 | item.ship_type_id = _loc2.api_stype;
129 |
130 | if (mst_stype.ContainsKey(item.ship_type_id))
131 | {
132 | var _loc3 = mst_stype[item.ship_type_id];
133 | item.ship_type_name = _loc3.api_name;
134 | item.ship_type_sortno = _loc3.api_sortno;
135 | }
136 |
137 | graphList.Add(item);
138 | mst_ship.Remove(item.ship_id);
139 | }
140 | else
141 | {
142 | #if DEBUG
143 | Debug.WriteLine(@"CACHR> shipgraph->ship匹配失败
144 | > {0} = {1} {2} {3}
145 | ", _loc1.ToString(), _loc1.api_id, _loc1.api_sortno, _loc1.api_filename);
146 | #endif
147 | }
148 | }
149 |
150 | #if DEBUG
151 | Debug.WriteLine("CACHR> graphList = {0}, mst_shipgraph = {1}",
152 | graphList.Count.ToString(),
153 | mst_shipgraph.Count.ToString()
154 | );
155 | #endif
156 | }
157 |
158 | ///
159 | /// 开始生成 GraphList.txt 文件
160 | ///
161 | static public void GenerateList()
162 | {
163 | //var path = Settings.Current.CacheFolder + "\\api_start2.session";
164 | var path = @"E:\Game\KanColleViewer ver.3.3\MyCache\api_start2.session";
165 | if (!File.Exists(path))
166 | {
167 | MessageBox.Show("无法生成舰娘列表,因为没有保存 api_start2 通信数据。", "提督很忙!缓存工具");
168 | return;
169 | }
170 |
171 | //kcsapi_start2 data;
172 | Session oSession;
173 | //try
174 | //{
175 | //data = (kcsapi_start2)ReadSessionData();
176 | oSession = ReadSessionData(path) as Session;
177 | //}
178 | //catch (Exception ex)
179 | //{
180 | // MessageBox.Show("未能生成舰娘列表。读取本地保存的 api_start2 通信数据时发生异常。", "提督很忙!缓存工具");
181 | // //Log.Exception(ex.Source, ex, "读取本地保存的 api_start2 通信数据时发生异常");
182 | // throw ex;
183 | //}
184 | try
185 | {
186 | ParseSession(oSession);
187 | }
188 | catch (Exception ex)
189 | {
190 | MessageBox.Show("未能生成舰娘列表。解析 api_start2 数据时发生异常。", "提督很忙!缓存工具");
191 | //Log.Exception(ex.Source, ex, "解析 api_start2 数据时发生异常。");
192 | return;
193 | }
194 | try
195 | {
196 | PrintToFile();
197 |
198 | //string filepath = Settings.Current.CacheFolder + "\\GraphList.txt";
199 | string filepath = @"E:\Game\KanColleViewer ver.3.3\MyCache\GraphList.txt";
200 | var si = new System.Diagnostics.ProcessStartInfo()
201 | {
202 | FileName = filepath,
203 | UseShellExecute = true,
204 | };
205 | System.Diagnostics.Process.Start(si);
206 | }
207 | catch (Exception ex)
208 | {
209 | //Log.Exception(ex.Source, ex, "写入GraphList.txt时或启动进程时发生异常");
210 | return;
211 | }
212 | }
213 |
214 | ///
215 | /// 保存 api_start2 通信数据到本地
216 | ///
217 | static void SaveSessionData(Session session)
218 | {
219 | //var path = Settings.Current.CacheFolder + "\\api_start2.dat";
220 |
221 | session.SaveSession(Settings.Current.CacheFolder + "\\api_start2.session", false);
222 |
223 | //var data = session.GetRequestBodyAsString();
224 | //data = data.StartsWith("svdata=")
225 | // ? data.Substring(7) : data.Replace("svdata=", "");
226 |
227 | //Debug.WriteLineIf(data.Length < 100, data);
228 |
229 | //File.WriteAllText(path, data);
230 | }
231 |
232 | ///
233 | /// 从本地读取 api_start2 通信数据
234 | ///
235 | static object ReadSessionData(string path)
236 | {
237 | //var path = Settings.Current.CacheFolder + "\\api_start2.dat";
238 | //var bytes = Encoding.UTF8.GetBytes(File.ReadAllText(path));
239 |
240 | //var serializer = new DataContractJsonSerializer(typeof(svdata));
241 | //using (var stream = new MemoryStream(bytes))
242 | //{
243 | // return serializer.ReadObject(stream) as svdata;
244 | //}
245 |
246 | Session session = new Session(new byte[] { 0 }, new byte[] { 0 });
247 | if (!session.LoadRequestBodyFromFile(path))
248 | {
249 | throw new ApplicationException("LoadRequestBodyFromFile Failed!! " + path);
250 | }
251 | if (!session.LoadResponseFromFile(path))
252 | {
253 | throw new ApplicationException("LoadResponseFromFile Failed!! " + path);
254 | }
255 | return session;
256 | }
257 |
258 | ///
259 | /// Fiddler规则(通信完成后
260 | ///
261 | static public void RulePrintGraphList(Session oSession)
262 | {
263 | if (oSession.PathAndQuery != "/kcsapi/api_start2")
264 | return;
265 |
266 | //Debug.WriteLine("【START2】" + oSession.PathAndQuery);
267 |
268 | SaveSessionData(oSession);
269 |
270 | //移除规则
271 | RemoveRule();
272 | }
273 |
274 | static public void AppendRule()
275 | {
276 | FiddlerApplication.AfterSessionComplete += RulePrintGraphList;
277 | //Debug.WriteLine("CACHR> RulePrintGraphList Appended");
278 | }
279 |
280 | static public void RemoveRule()
281 | {
282 | FiddlerApplication.AfterSessionComplete -= RulePrintGraphList;
283 | //Debug.WriteLine("CACHR> RulePrintGraphList Removed");
284 | }
285 | }
286 |
287 |
288 | class ship_graph_item
289 | {
290 | public int ship_id = 0;
291 | public int ship_sortno = 0;
292 | public string ship_name = "";
293 |
294 | public int ship_type_id = 0;
295 | public int ship_type_sortno = 0;
296 | public string ship_type_name = "";
297 |
298 | public int ship_graph_sortno = 0;
299 | public string ship_filename = "";
300 | public string ship_version = "";
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/DebugPlatform/Last-Modified - 副本.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | /kcs/scenes/TitleMain.swf
4 | /kcs/PortMain.swf
5 | /kcs/resources/swf/commonAssets.swf
6 | /kcs/resources/swf/font.swf
7 | /kcs/resources/swf/icons.swf
8 | /kcs/resources/swf/sound_se.swf
9 | /kcs/scenes/ArsenalMain.swf
10 | /kcs/scenes/DutyMain.swf
11 | /kcs/scenes/OrganizeMain.swf
12 | /kcs/scenes/RemodelMain.swf
13 | /kcs/scenes/RepairMain.swf
14 | /kcs/scenes/SupplyMain.swf
15 |
16 |
--------------------------------------------------------------------------------
/DebugPlatform/Last-Modified.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /kcs/PortMain.swf
5 |
6 |
7 |
8 |
9 | /kcs/resources/swf/commonAssets.swf
10 |
11 |
12 |
13 |
14 | /kcs/resources/swf/font.swf
15 |
16 |
17 |
18 |
19 | /kcs/resources/swf/icons.swf
20 |
21 |
22 |
23 |
24 | /kcs/resources/swf/sound_se.swf
25 |
26 |
27 |
28 |
29 | /kcs/scenes/ArsenalMain.swf
30 |
31 |
32 |
33 |
34 | /kcs/scenes/DutyMain.swf
35 |
36 |
37 |
38 |
39 | /kcs/scenes/OrganizeMain.swf
40 |
41 |
42 |
43 |
44 | /kcs/scenes/RemodelMain.swf
45 |
46 |
47 |
48 |
49 | /kcs/scenes/RepairMain.swf
50 |
51 |
52 |
53 |
54 | /kcs/scenes/SupplyMain.swf
55 |
56 |
57 |
58 |
59 | /kcs/scenes/TitleMain.swf
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/DebugPlatform/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows.Data;
7 | using System.Windows;
8 | //using System.Text.RegularExpressions;
9 |
10 | using System.Xml;
11 | using System.Xml.Linq;
12 | using System.IO;
13 |
14 | using d_f_32.KanColleCacher;
15 | using d_f_32.KanColleCacher.Configuration;
16 |
17 | namespace DebugPlatform
18 | {
19 | class Program
20 | {
21 |
22 | static void Main(string[] args)
23 | {
24 | Console.WriteLine("-----------------------------------------");
25 | GraphList.DebugFunc();
26 | Console.ReadLine();
27 | }
28 | static void Main_old(string[] args)
29 | {
30 | Console.WriteLine("-----------------------------------------");
31 | Console.WriteLine("KanColleCacher_1.3_Updater Copyright d.f.32 - 2015 \n");
32 | Console.WriteLine("本程序是KCV缓存插件 KanColleCacher 的更新补丁程序。");
33 | Console.WriteLine("插件从1.3更新到2.0时废弃了原有的设置文件与部分数据文件的储存方式,这将造成设置的丢失以及部分缓存文件需要重新下载。");
34 | Console.WriteLine("本程序则是用来解决这一问题的。");
35 | Console.WriteLine();
36 | Console.WriteLine(@"在程序运行前,请核实以下描述:
37 | * 你在使用名为【舰队很忙!KanColleViewer】的游戏程序
38 | * 你安装使用了插件 KanColleCacher 一段时间,插件为你缓存了大量的游戏文件
39 | * 你需要将插件 KanColleCacher 从1.3更新到2.0,且不希望再次这些下载缓存文件
40 | * 你知道本程序只是补丁,并不包含插件主体的dll文件,且也不会为你拷贝移动插件主体
41 | * 你已经在运行前将本程序放置在了【舰队很忙!KanColleViewer】的程序目录下,或其目录的Plugins子目录下
42 |
43 | 若以上描述核实无误,请按回车键以开始执行补丁...");
44 | Console.ReadLine();
45 |
46 | List setFiles = new List();
47 | List cacheFolders = new List();
48 | string dir = Directory.GetCurrentDirectory();
49 |
50 | Console.WriteLine("-----------------------------------------");
51 | Console.WriteLine("检索设置文件...");
52 |
53 | var path = dir + @"\Plugins\KanColleCacher.xml";
54 | if (File.Exists(path)) setFiles.Add(path);
55 |
56 | path = dir + @"\KanColleCacher.xml";
57 | if (File.Exists(path)) setFiles.Add(path);
58 |
59 | path = Path.Combine(
60 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
61 | "grabacr.net",
62 | "KanColleViewer",
63 | "KanColleCacher.xml"
64 | );
65 | if (File.Exists(path)) setFiles.Add(path);
66 |
67 | if (setFiles.Count == 0)
68 | {
69 | Console.WriteLine("没有找到任何设置文件!更新无法继续进行。");
70 | Console.WriteLine("请按下回车键以退出程序...");
71 | Console.ReadLine();
72 | return;
73 | }
74 |
75 | Console.WriteLine("找到设置文件共 {0} 个",setFiles.Count);
76 |
77 | foreach (var fpath in setFiles)
78 | {
79 | Console.WriteLine("处理设置文件...\n\t" + fpath);
80 | try
81 | {
82 | string cachefolder;
83 | ProcessSettingFile(fpath, out cachefolder);
84 | if (String.IsNullOrEmpty(cachefolder) || !Directory.Exists(cachefolder))
85 | {
86 | Console.WriteLine("无效的缓存文件夹!\n\t" + cachefolder);
87 | }
88 | cacheFolders.Add(cachefolder);
89 | }
90 | catch (Exception ex)
91 | {
92 | Console.WriteLine("处理设置文件时发生异常。可能是文件损坏。");
93 | Console.WriteLine(ex.Message);
94 | }
95 | }
96 |
97 | Console.WriteLine("设置文件处理结束。找到缓存文件夹 {0} 个", cacheFolders.Count);
98 | Console.WriteLine();
99 | Console.WriteLine("-----------------------------------------");
100 |
101 | if (cacheFolders.Count == 0)
102 | {
103 | Console.WriteLine("没有找到任何缓存文件夹的设置!更新无法继续进行。");
104 | Console.WriteLine("请手动输入缓存文件夹地址或按回车键结束程序:");
105 | string input;
106 | while (!String.IsNullOrWhiteSpace(input = Console.ReadLine()))
107 | {
108 | if (Directory.Exists(input))
109 | {
110 | cacheFolders.Add(input);
111 | break;
112 | }
113 | Console.WriteLine("无效地址");
114 | }
115 | if (cacheFolders.Count==0) return;
116 | }
117 |
118 | Console.WriteLine();
119 |
120 | foreach (var folder in cacheFolders)
121 | {
122 | Console.WriteLine("处理缓存文件夹...\n\t" + folder);
123 | ProcessCacheFolder(folder);
124 | }
125 |
126 | Console.WriteLine("缓存文件处理结束\n");
127 | Console.WriteLine("-----------------------------------------");
128 | Console.WriteLine("是否移除废弃的文件? [Y] 删除 [N] 不删除");
129 |
130 | string ipt = Console.ReadLine().ToLower();
131 | while (!ipt.StartsWith("y") && !ipt.StartsWith("n"))
132 | {
133 | ipt = Console.ReadLine().ToLower();
134 | }
135 |
136 | if (ipt.StartsWith("y"))
137 | {
138 | foreach (var f in setFiles)
139 | {
140 | try {
141 | File.Delete(f);
142 | Console.WriteLine("删除 " + f);
143 | }
144 | catch { }
145 | }
146 | foreach (var p in cacheFolders)
147 | {
148 | try {
149 | File.Delete(p + "\\Last-Modified.xml");
150 | Console.WriteLine("删除 " + p + "\\Last-Modified.xml");
151 | }
152 | catch { }
153 | }
154 | }
155 |
156 | Console.WriteLine("按任意键退出程序");
157 | Console.ReadKey();
158 | }
159 |
160 | static void ProcessSettingFile(string filepath, out string cachefolder)
161 | {
162 | var doc = XDocument.Load(filepath);
163 | var parser = new ConfigParser();
164 | var section = parser["Settings"] = new Section();
165 | parser.SerializeObject(new Settings(), "Settings");
166 |
167 | foreach (var elm in doc.Root.Elements())
168 | {
169 | var name = elm.Name.ToString();
170 | var val = elm.Value;
171 |
172 | if (!String.IsNullOrEmpty(val))
173 | {
174 | section[name] = val;
175 | }
176 | }
177 |
178 | section["SaveApiStart2"] = section["PrintGraphList"];
179 | section["PrintGraphList"] = null;
180 |
181 | parser.SaveIniFile(filepath.Substring(0, filepath.Length - 3) + "ini");
182 | cachefolder = section["CacheFolder"];
183 | }
184 |
185 | static void ProcessCacheFolder(string cachefolder)
186 | {
187 | var xmlf = cachefolder + "//Last-Modified.xml";
188 |
189 | if (!File.Exists(xmlf))
190 | {
191 | Console.WriteLine("没有找到Last-Modified.xml。");
192 | return;
193 | }
194 | XDocument doc;
195 | try
196 | {
197 | doc = XDocument.Load(xmlf);
198 | }
199 | catch (Exception)
200 | {
201 | Console.WriteLine("Last-Modified.xml文件损坏。");
202 | return;
203 | }
204 |
205 | int count = 0;
206 | foreach (var elm in doc.Root.Elements())
207 | {
208 | count++;
209 | try
210 | {
211 | var path = elm.Element("Path").Value;
212 | var time = elm.Element("Time").Value;
213 |
214 | path = (cachefolder + "\\" + path).Replace('/', '\\');
215 | if (!File.Exists(path))
216 | {
217 | Console.WriteLine("无效地址:" + path);
218 | continue;
219 | }
220 |
221 | var fi = new FileInfo(path);
222 | fi.LastWriteTime =GMTHelper.GMT2Local(time);
223 | }
224 | catch(Exception ex)
225 | {
226 | Console.WriteLine("第 {0} 个元素损坏。" + ex.Message, count);
227 | }
228 | }
229 | Console.WriteLine("Last-Modified.xml 处理结束,共处理 {0} 个文件", count);
230 | }
231 | }
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 | class ModifiedRecord
240 | {
241 | static string filepath;
242 | static XDocument fileXML;
243 | static IEnumerable recordList;
244 |
245 | const string _RootName = "Last-Modified";
246 | const string _ItemElm = "Record";
247 | const string _ElmPath = "Path";
248 | const string _ElmTime = "Time";
249 | const string _ElmVersion = "Version";
250 |
251 | #region 加载与保存
252 |
253 | ///
254 | /// 从Last-Modified.xml加载记录
255 | ///
256 | static public void Load()
257 | {
258 | //filepath = Settings.Current.CacheFolder + "\\Last-Modified.xml";
259 | filepath = @"B:\GitHub\KanColleCacher\DebugPlatform\Last-Modified - 副本.xml";
260 | try
261 | {
262 | if (File.Exists(filepath))
263 | fileXML = XDocument.Load(filepath);
264 | }
265 | catch (Exception ex)
266 | {
267 | //Log.Exception(ex.InnerException, ex, "加载Last-Modified.xml时发生异常");
268 | throw ex;
269 | }
270 |
271 | if (fileXML == null)
272 | {
273 | fileXML = new XDocument();
274 | fileXML.Add(new XElement(_RootName));
275 | }
276 |
277 | recordList = fileXML.Descendants(_ItemElm);
278 | //recordList 和 fileXML是同步的
279 | }
280 |
281 | ///
282 | /// 保存到Last-Modified.xml
283 | ///
284 | static public void Save()
285 | {
286 | filepath = @"B:\GitHub\KanColleCacher\DebugPlatform\Last-Modified.xml";
287 |
288 |
289 |
290 | try
291 | {
292 | var elms = fileXML.Descendants(_ItemElm)
293 | .OrderBy(elm =>
294 | { return elm.Element(_ElmPath).Value; }
295 | ).ToArray();
296 |
297 | fileXML.Root.Elements().Remove();
298 | fileXML.Root.Add(elms);
299 | }
300 | catch
301 | {
302 | //Log.Exception(ex.InnerException, ex, "保存Last-Modified.xml时发生异常");
303 | }
304 |
305 | fileXML.Save(filepath);
306 | }
307 |
308 | }
309 | #endregion
310 |
311 |
312 |
313 | }
314 |
--------------------------------------------------------------------------------
/DebugPlatform/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过以下
6 | // 特性集控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("KanColleCacher_1.3_Updater")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("KanColleCacher")]
13 | [assembly: AssemblyCopyright("Copyright © d.f.32 - 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("5fd4a204-ab3a-4460-a561-027d88009494")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 生成号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/DebugPlatform/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Debug = System.Diagnostics.Debug;
8 | using d_f_32.KanColleCacher.Configuration;
9 | using System.ComponentModel;
10 | using System.ComponentModel.Composition;
11 | using System.Runtime.CompilerServices;
12 |
13 |
14 | namespace d_f_32.KanColleCacher
15 | {
16 | [Serializable]
17 | public class Settings : INotifyPropertyChanged
18 | {
19 | private static string filePath;
20 |
21 | public static Settings Current { get; private set; }
22 |
23 | ///
24 | /// 加载插件设置
25 | ///
26 | public static void Load()
27 | {
28 | filePath = Directory.GetCurrentDirectory() + @"\Plugins\KanColleCacher.ini";
29 |
30 | if (!File.Exists(filePath))
31 | {
32 | var path = Path.Combine(
33 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
34 | "grabacr.net",
35 | "KanColleViewer",
36 | "KanColleCacher.ini"
37 | );
38 | if (File.Exists(path))
39 | filePath = path;
40 | }
41 |
42 | if (File.Exists(filePath))
43 | {
44 | var _Parser = ConfigParser.ReadIniFile(filePath);
45 | Current = _Parser.DeserializeObject("Settings");
46 |
47 | try
48 | {
49 | Directory.CreateDirectory(Current.CacheFolder);
50 | }
51 | catch (Exception ex)
52 | {
53 | Current.CacheFolder = Directory.GetCurrentDirectory() + @"\MyCache";
54 | //Log.Exception(ex.InnerException, ex, "设置文件中CacheFolder不存在,试图创建时发生异常");
55 | }
56 | }
57 | else
58 | {
59 | //设置文件丢失
60 | }
61 |
62 | Current = Current ?? new Settings();
63 | }
64 |
65 | ///
66 | /// 保存设置
67 | ///
68 | public static void Save()
69 | {
70 | try
71 | {
72 | var _Parser = File.Exists(filePath)
73 | ? ConfigParser.ReadIniFile(filePath)
74 | : new ConfigParser();
75 |
76 | _Parser.SerializeObject(Current, "Settings");
77 | _Parser.SaveIniFile(filePath);
78 | }
79 | catch (Exception ex)
80 | {
81 | //Log.Exception(ex.InnerException, ex, "保存设置文件时出现异常");
82 | }
83 | }
84 |
85 |
86 | public Settings ()
87 | {
88 | _CacheFolder = Directory.GetCurrentDirectory() + @"\MyCache";
89 | _CacheEnabled = true;
90 | _HackEnabled = true;
91 | _HackTitleEnabled = true;
92 |
93 | _CacheEntryFiles = 2;
94 | _CachePortFiles = 2;
95 | _CacheSceneFiles = 2;
96 | _CacheResourceFiles = 2;
97 | _CacheSoundFiles = 2;
98 |
99 | _CheckFiles = 1;
100 | _SaveApiStart2 = true;
101 | }
102 |
103 |
104 |
105 | private string _CacheFolder;
106 | [ExportMetadata("Comment","缓存文件夹")]
107 | public string CacheFolder
108 | {
109 | get { return this._CacheFolder; }
110 | set
111 | {
112 | if (this._CacheFolder != value)
113 | {
114 | this._CacheFolder = value;
115 | this.RaisePropertyChanged();
116 | }
117 | }
118 | }
119 |
120 | private bool _CacheEnabled;
121 | [ExportMetadata("Comment", "启用缓存功能")]
122 | public bool CacheEnabled
123 | {
124 | get { return this._CacheEnabled; }
125 | set
126 | {
127 | if (this._CacheEnabled != value)
128 | {
129 | this._CacheEnabled = value;
130 | this.RaisePropertyChanged();
131 | }
132 | }
133 | }
134 |
135 | private bool _HackEnabled;
136 | [ExportMetadata("Comment", "启用Hack规则")]
137 | public bool HackEnabled
138 | {
139 | get { return this._HackEnabled; }
140 | set
141 | {
142 | if (this._HackEnabled != value)
143 | {
144 | this._HackEnabled = value;
145 | this.RaisePropertyChanged();
146 | }
147 | }
148 | }
149 |
150 | private bool _HackTitleEnabled;
151 | [ExportMetadata("Comment", "启用针对TitleCall与WorldName的特殊规则")]
152 | public bool HackTitleEnabled
153 | {
154 | get { return this._HackTitleEnabled; }
155 | set
156 | {
157 | if (this._HackTitleEnabled != value)
158 | {
159 | this._HackTitleEnabled = value;
160 | this.RaisePropertyChanged();
161 | }
162 | }
163 | }
164 |
165 | private int _CacheEntryFiles;
166 | public int CacheEntryFiles
167 | {
168 | get { return this._CacheEntryFiles; }
169 | set
170 | {
171 | if (this._CacheEntryFiles != value)
172 | {
173 | this._CacheEntryFiles = value;
174 | this.RaisePropertyChanged();
175 | }
176 | }
177 | }
178 |
179 | private int _CachePortFiles;
180 | public int CachePortFiles
181 | {
182 | get { return this._CachePortFiles; }
183 | set
184 | {
185 | if (this._CachePortFiles != value)
186 | {
187 | this._CachePortFiles = value;
188 | this.RaisePropertyChanged();
189 | }
190 | }
191 | }
192 |
193 | private int _CacheSceneFiles;
194 | public int CacheSceneFiles
195 | {
196 | get { return this._CacheSceneFiles; }
197 | set
198 | {
199 | if (this._CacheSceneFiles != value)
200 | {
201 | this._CacheSceneFiles = value;
202 | this.RaisePropertyChanged();
203 | }
204 | }
205 | }
206 |
207 | private int _CacheResourceFiles;
208 | public int CacheResourceFiles
209 | {
210 | get { return this._CacheResourceFiles; }
211 | set
212 | {
213 | if (this._CacheResourceFiles != value)
214 | {
215 | this._CacheResourceFiles = value;
216 | this.RaisePropertyChanged();
217 | }
218 | }
219 | }
220 |
221 | private int _CacheSoundFiles;
222 | public int CacheSoundFiles
223 | {
224 | get { return this._CacheSoundFiles; }
225 | set
226 | {
227 | if (this._CacheSoundFiles != value)
228 | {
229 | this._CacheSoundFiles = value;
230 | this.RaisePropertyChanged();
231 | }
232 | }
233 | }
234 |
235 | private int _CheckFiles;
236 | [ExportMetadata("Comment", @"向服务器发送文件验证请求
237 | ; 0 - 不验证;1 - 不验证资源SWF文件;2 - 验证所有SWF文件
238 | ; 验证文件可以保证缓存的游戏文件始终是有效可用的,但因为要与服务器通信所以会比不验证花费更长的加载时间")]
239 | public int CheckFiles
240 | {
241 | get { return this._CheckFiles; }
242 | set
243 | {
244 | if (this._CheckFiles != value)
245 | {
246 | this._CheckFiles = value;
247 | this.RaisePropertyChanged();
248 | }
249 | }
250 | }
251 |
252 | private bool _SaveApiStart2;
253 | [ExportMetadata("Comment", @"保存 api_start2 通信数据以便生成 舰娘立绘的文件名列表。
254 | ; 只有当缓存文件夹中的 api_start2.dat 不存在时才会进行保存。
255 | ; 这一设置只有在游戏加载时才有效。")]
256 | public bool SaveApiStart2
257 | {
258 | get { return this._SaveApiStart2; }
259 | set
260 | {
261 | if (this._SaveApiStart2 != value)
262 | {
263 | this._SaveApiStart2 = value;
264 | this.RaisePropertyChanged();
265 | }
266 | }
267 | }
268 |
269 | #region 实现通知
270 |
271 | public event PropertyChangedEventHandler PropertyChanged;
272 |
273 | void RaisePropertyChanged([CallerMemberName] String propertyName = "")
274 | {
275 | if (PropertyChanged != null)
276 | {
277 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
278 | }
279 | }
280 |
281 | #endregion
282 | }
283 | }
284 |
--------------------------------------------------------------------------------
/KanColleCacher.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Express 2013 for Windows Desktop
4 | VisualStudioVersion = 12.0.21005.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KanColleCacher", "KanColleCacher\KanColleCacher.csproj", "{1FF4CF0A-E57E-433C-B5B0-5363C2425780}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugPlatform", "DebugPlatform\DebugPlatform.csproj", "{D8F14FD8-B541-40B0-9D52-32FF0A7EB6B3}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {1FF4CF0A-E57E-433C-B5B0-5363C2425780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {1FF4CF0A-E57E-433C-B5B0-5363C2425780}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {1FF4CF0A-E57E-433C-B5B0-5363C2425780}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {1FF4CF0A-E57E-433C-B5B0-5363C2425780}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {D8F14FD8-B541-40B0-9D52-32FF0A7EB6B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {D8F14FD8-B541-40B0-9D52-32FF0A7EB6B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {D8F14FD8-B541-40B0-9D52-32FF0A7EB6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {D8F14FD8-B541-40B0-9D52-32FF0A7EB6B3}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/KanColleCacher/CacheCore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using Debug = System.Diagnostics.Debug;
8 |
9 |
10 | namespace d_f_32.KanColleCacher
11 | {
12 |
13 | enum filetype
14 | {
15 | not_file,
16 | unknown_file,
17 |
18 | game_entry, //kcs\mainD2.swf
19 | //kcs\Core.swf
20 |
21 | entry_large, //kcs\scenes\TitleMain.swf
22 | //kcs\resources\swf\commonAsset.swf
23 | //kcs\resources\swf\font.swf
24 | //kcs\resources\swf\icons.swf
25 |
26 | port_main, //kcs\PortMain.swf
27 | //kcs\resources\swf\sound_se.swf
28 |
29 | scenes, //kcs\scenes\
30 |
31 | resources, //kcs\resources\bgm_p\
32 | //kcs\resources\swf\sound_bgm.swf
33 | //kcs\resources\swf\sound_b_bgm_*.swf
34 | //kcs\resources\swf\map\
35 | //kcs\resources\swf\ships\
36 |
37 | image, //kcs\resources\images
38 | sound, //kcs\sound
39 |
40 | world_name, //kcs\resources\images\world
41 | title_call, //kcs\sound\titlecall
42 | }
43 |
44 |
45 | class CacheCore
46 | {
47 | #region 初始化与析构
48 | Settings set;
49 | string myCacheFolder;
50 |
51 | public CacheCore()
52 | {
53 | set = Settings.Current;
54 | //VersionChecker.Load();
55 | myCacheFolder = set.CacheFolder;
56 | }
57 |
58 |
59 | #endregion
60 |
61 | ///
62 | /// 对于一个新的客户端请求,根据url,决定下一步要对请求怎样处理
63 | ///
64 | /// 请求的url
65 | /// 本地文件地址 or 记录的修改日期
66 | /// 下一步我们该做什么?忽略请求;返回缓存文件;验证缓存文件
67 | public Direction GotNewRequest(string url, out string result)
68 | {
69 | result = "";
70 | string filepath = "";
71 |
72 | Uri uri;
73 | try { uri = new Uri(url); }
74 | catch (Exception ex)
75 | {
76 | System.Diagnostics.Debug.WriteLine(ex);
77 | return Direction.Discharge_Response;
78 | //url无效,忽略请求(不进行任何操作)
79 | }
80 |
81 | if (!uri.IsFilePath())
82 | {
83 | return Direction.Discharge_Response;
84 | //url非文件,忽略请求
85 | }
86 |
87 | //识别文件类型
88 | filetype type = _RecognizeFileType(uri);
89 | if (type == filetype.unknown_file ||
90 | type == filetype.not_file ||
91 | type == filetype.game_entry)
92 | {
93 | return Direction.Discharge_Response;
94 | //无效的文件,忽略请求
95 | }
96 |
97 | //检查Title Call与World Name的特殊地址
98 | if (set.HackTitleEnabled)
99 | {
100 | if (type == filetype.title_call)
101 | {
102 | filepath = uri.AbsolutePath.Replace('/', '\\');
103 | filepath = filepath.Remove(filepath.LastIndexOf('\\')) + ".mp3";
104 | filepath = myCacheFolder + filepath;
105 | result = filepath;
106 |
107 | if (File.Exists(filepath))
108 | return Direction.Return_LocalFile;
109 | }
110 | else if (type == filetype.world_name)
111 | {
112 | filepath = myCacheFolder + @"\kcs\resources\image\world.png";
113 | result = filepath;
114 |
115 | if (File.Exists(filepath))
116 | return Direction.Return_LocalFile;
117 | }
118 | }
119 |
120 | //检查一般文件地址
121 | if ((type == filetype.resources && set.CacheResourceFiles > 0) ||
122 | (type == filetype.entry_large && set.CacheEntryFiles > 0) ||
123 | (type == filetype.port_main && set.CachePortFiles > 0) ||
124 | (type == filetype.scenes && set.CacheSceneFiles > 0) ||
125 | (type == filetype.sound && set.CacheSoundFiles > 0) ||
126 | ((type == filetype.title_call ||
127 | type == filetype.world_name ||
128 | type == filetype.image) && set.CacheResourceFiles > 0))
129 | {
130 | filepath = myCacheFolder + uri.AbsolutePath.Replace('/', '\\');
131 |
132 | //检查Hack文件地址
133 | if (set.HackEnabled)
134 | {
135 | var fnext = uri.Segments.Last().Split('.');
136 | string hfilepath = filepath.Replace(uri.Segments.Last(), fnext[0] + ".hack." + fnext.Last());
137 |
138 | if (File.Exists(hfilepath))
139 | {
140 | result = hfilepath;
141 | return Direction.Return_LocalFile;
142 | //存在hack文件,则返回本地文件
143 | }
144 |
145 | }
146 |
147 | //检查缓存文件
148 | if (File.Exists(filepath))
149 | {
150 | //存在本地缓存文件 -> 检查文件的最后修改时间
151 | //(验证所有文件 或 只验证非资源文件)
152 | if (set.CheckFiles > 1 || (set.CheckFiles > 0 && type != filetype.resources))
153 | {
154 | //只有swf文件需要验证时间
155 | if (filepath.EndsWith(".swf"))
156 | {
157 | //文件存在且需要验证时间
158 | //-> 请求服务器验证修改时间(记录读取和保存的位置)
159 | result = filepath;
160 | _RecordTask(url, filepath);
161 | return Direction.Verify_LocalFile;
162 | }
163 |
164 | ////检查文件时间
165 | //int i = VersionChecker.GetFileLastTime(uri, out result);
166 |
167 | //if (i == 1)
168 | //{
169 | // //存在这个文件的修改时间记录
170 | // //-> 请求服务器验证修改时间(记录读取和保存的位置)
171 | // _RecordTask(url, filepath);
172 | // return Direction.Verify_LocalFile;
173 | //}
174 | //else if (i == 0)
175 | //{
176 | // //没有关于这个文件最后修改时间的记录
177 | // //-> 当做这个文件不存在
178 | // //-> 下载文件(记录保存地址)
179 | // _RecordTask(url, filepath);
180 | // return Direction.Discharge_Response;
181 | //}
182 | //else
183 | //{
184 | // //文件类型不需要验证时间(只有swf验证)
185 | //}
186 | }
187 | //文件不需验证
188 | //->返回本地缓存文件
189 | result = filepath;
190 | return Direction.Return_LocalFile;
191 |
192 | }
193 | else
194 | {
195 | //缓存文件不存在
196 | //-> 下载文件 (记录保存地址)
197 | _RecordTask(url, filepath);
198 | return Direction.Discharge_Response;
199 | }
200 | }
201 |
202 | //文件类型对应的缓存设置没有开启
203 | //-> 当做文件不存在
204 | return Direction.Discharge_Response;
205 | }
206 |
207 | filetype _RecognizeFileType(Uri uri)
208 | {
209 | if (!uri.IsFilePath())
210 | return filetype.not_file;
211 |
212 | var seg = uri.Segments;
213 |
214 | if (seg[1] != "kcs/")
215 | {
216 | return filetype.not_file;
217 | }
218 | else
219 | {
220 |
221 | if (seg[2] == "resources/")
222 | {
223 | if (seg[3] == "swf/")
224 | {
225 | if (seg[4] == "commonAssets.swf" ||
226 | seg[4] == "font.swf" ||
227 | seg[4] == "icons.swf")
228 | {
229 | return filetype.entry_large;
230 | }
231 |
232 | else if (seg[4] == ("sound_se.swf"))
233 | {
234 | return filetype.port_main;
235 | }
236 | }
237 | else if (seg[3] == "image/")
238 | {
239 | if (seg[4] == "world/")
240 | {
241 | return filetype.world_name;
242 | }
243 |
244 | return filetype.image;
245 | }
246 | return filetype.resources;
247 | }
248 | else if (seg[2] == "scenes/")
249 | {
250 | if (seg[3] == "TitleMain.swf")
251 | {
252 | return filetype.entry_large;
253 | }
254 |
255 | return filetype.scenes;
256 | }
257 | else if (seg[2] == "sound/")
258 | {
259 | if (seg[3] == "titlecall/")
260 | {
261 | return filetype.title_call;
262 | }
263 |
264 | return filetype.sound;
265 | }
266 | else
267 | {
268 | if (seg[2] == "Core.swf" ||
269 | seg[2] == "mainD2.swf")
270 | {
271 | return filetype.game_entry;
272 | // kcs/mainD2.swf; kcs/Core.swf;
273 | }
274 | else if (seg[2] == "PortMain.swf")
275 | {
276 | return filetype.port_main;
277 | // kcs/PortMain.swf;
278 |
279 | }
280 | }
281 |
282 | //Debug.WriteLine("CACHR> _RecogniseFileType检查到无法识别的文件");
283 | //Debug.WriteLine(" "+uri.AbsolutePath);
284 | return filetype.unknown_file;
285 | }
286 |
287 | }
288 |
289 | public void RecordNewModifiedTime(string url, string time)
290 | {
291 | Uri uri;
292 | try { uri = new Uri(url); }
293 | catch { return; }
294 |
295 | //VersionChecker.Add(uri, time);
296 | }
297 |
298 | public bool AllowedToSave(filetype type)
299 | {
300 | return (type == filetype.resources && set.CacheResourceFiles > 1) ||
301 | (type == filetype.entry_large && set.CacheEntryFiles > 1) ||
302 | (type == filetype.port_main && set.CachePortFiles > 1) ||
303 | (type == filetype.scenes && set.CacheSceneFiles > 1) ||
304 | (type == filetype.sound && set.CacheSoundFiles > 1) ||
305 | (type == filetype.title_call ||
306 | type == filetype.world_name ||
307 | type == filetype.image) && set.CacheResourceFiles > 1;
308 | }
309 |
310 | void _RecordTask(string url, string filepath)
311 | {
312 | TaskRecord.Add(url, filepath);
313 | }
314 | }
315 | }
--------------------------------------------------------------------------------
/KanColleCacher/CacherToolView.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
35 |
47 |
56 |
62 |
77 |
130 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
180 |
181 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
195 |
196 |
199 |
200 |
201 |
202 |
205 |
207 |
208 |
219 |
220 |
224 |
228 |
231 |
234 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
249 |
250 |
251 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
265 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
278 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
309 |
310 |
311 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/KanColleCacher/CacherToolView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 |
8 | namespace d_f_32.KanColleCacher
9 | {
10 | ///
11 | /// ModifierView.xaml 的交互逻辑
12 | ///
13 | public partial class CacherToolView : UserControl
14 | {
15 | public CacherToolView()
16 | {
17 | try
18 | {
19 | InitializeComponent();
20 | }
21 | catch (Exception ex)
22 | {
23 | Log.Exception(ex.Source, ex, "ToolView初始化时发生异常");
24 | }
25 |
26 | }
27 |
28 | private void SelectCacheFolder_Click(object sender, RoutedEventArgs e)
29 | {
30 | var dlg = new System.Windows.Forms.FolderBrowserDialog()
31 | {
32 | SelectedPath = Settings.Current.CacheFolder,
33 | ShowNewFolderButton = true,
34 | Description = "选择一个文件夹用于保存缓存文件。新的地址将在程序下次启动时生效。"
35 | };
36 | if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK
37 | && Directory.Exists(dlg.SelectedPath))
38 | {
39 | Settings.Current.CacheFolder = dlg.SelectedPath;
40 | }
41 | }
42 |
43 | private void GenerateFileList_Click(object sender, RoutedEventArgs e)
44 | {
45 | GraphList.GenerateList();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/KanColleCacher/ConfigParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Text.RegularExpressions;
7 | using System.Dynamic;
8 | using System.Reflection;
9 | using System.ComponentModel.Composition;
10 |
11 | namespace d_f_32.KanColleCacher.Configuration
12 | {
13 | ///
14 | /// 表示一个INI文档节点
15 | ///
16 | public class Section
17 | {
18 | readonly Dictionary Options = new Dictionary();
19 | readonly Dictionary Comments = new Dictionary();
20 |
21 | #region 通过索引访问
22 | ///
23 | /// 获取或设置指定指定的选项的选项值。选项名称不区分大小写。
24 | ///
25 | /// 选项的名称
26 | /// 指定的选项。如果找不到指定选项,get 将返回null,set 将创建指定值的选项
27 | public string this[string option]
28 | {
29 | get
30 | {
31 | foreach (var pair in Options)
32 | {
33 | if (String.Compare(pair.Key, option, true) == 0)
34 | {
35 | return pair.Value;
36 | }
37 | }
38 | return null;
39 | }
40 | set
41 | {
42 | foreach (var key in Options.Keys)
43 | {
44 | if (String.Compare(key, option, true) == 0)
45 | {
46 | if (value == null)
47 | {
48 | Options.Remove(key);
49 | //删除注释
50 | }
51 | else
52 | {
53 | Options[key] = value;
54 | }
55 | return;
56 | }
57 | }
58 | if (value != null)
59 | Options.Add(option, value);
60 | }
61 | }
62 |
63 | #endregion
64 |
65 | #region 注释操作
66 |
67 | ///
68 | /// 添加指定选项后的注释。空值选项表示节点注释。注释将自动换行。
69 | ///
70 | /// 选项名
71 | /// 注释
72 | public void AddComment(string option, string comment)
73 | {
74 | foreach (var key in Comments.Keys)
75 | {
76 | if (String.Compare(key, option, true) == 0)
77 | {
78 | Comments[key] += "\r\n" + comment;
79 | return;
80 | }
81 | }
82 | Comments.Add(option.ToLower(), comment);
83 | }
84 | public void SetComment(string option, string comment)
85 | {
86 | Comments[option.ToLower()] = comment;
87 | }
88 |
89 | #endregion
90 |
91 | #region 文本序列化
92 | ///
93 | /// 将节点内容序列化为文本
94 | ///
95 | /// 表示节点内容的文本
96 | public override string ToString()
97 | {
98 | StringBuilder contents = new StringBuilder();
99 |
100 | //节点注释
101 | if (Comments.ContainsKey(""))
102 | {
103 | contents.AppendLine(Comments[""]);
104 | }
105 |
106 | foreach (var pairs in Options)
107 | {
108 | contents.AppendFormat(@"{0}={1}", pairs.Key, pairs.Value);
109 | contents.AppendLine();
110 |
111 | var key = pairs.Key.ToLower();
112 | if (Comments.ContainsKey(key))
113 | {
114 | contents.AppendLine(Comments[key]);
115 | }
116 | }
117 | return contents.ToString(0, contents.Length -2);
118 | }
119 |
120 | #endregion
121 |
122 | #region 枚举访问
123 |
124 | public IEnumerator> GetEnumerator()
125 | {
126 | return Options.GetEnumerator();
127 | }
128 |
129 | #endregion 枚举访问
130 | }
131 |
132 |
133 | #region DynamicSection
134 | //public class Section : DynamicObject
135 | //{
136 | // readonly Dictionary Options = new Dictionary();
137 | // readonly Dictionary Comments = new Dictionary();
138 |
139 | // #region [公开]文本解析
140 |
141 | // ////将一行文本解析并添加到当前节点中
142 | // //string ParseLine(string line, string lastOption)
143 | // //{
144 | // // line = line.Trim();
145 | // // int pos = line.IndexOf('=');
146 |
147 | // // //当前行是 Option=Value
148 | // // if (pos > 0)
149 | // // {
150 | // // string option = line.Substring(0, pos).TrimEnd();
151 | // // string value = line.Substring(pos + 1).TrimStart();
152 | // // _TrySetValue(option, value);
153 | // // return option;
154 | // // }
155 | // // //当前行是 无效内容
156 | // // else
157 | // // {
158 | // // AddComment(lastOption, line);
159 | // // return "";
160 | // // }
161 | // //}
162 |
163 | // ////将多行文本解析并添加到当前节点中
164 | // //public void Parse(string[] lines)
165 | // //{
166 | // // string curOption = "";
167 |
168 | // // foreach (var line in lines)
169 | // // {
170 | // // curOption = ParseLine(line, curOption);
171 | // // }
172 | // //}
173 |
174 | // ////将一段文本解析并添加到当前节点中
175 | // //public void Parse(string content)
176 | // //{
177 | // // string curOption = "";
178 |
179 | // // using (var reader = new StringReader(content))
180 | // // {
181 | // // string line = reader.ReadLine();
182 | // // while (line != null)
183 | // // {
184 | // // curOption = ParseLine(line, curOption);
185 | // // line = reader.ReadLine();
186 | // // }
187 | // // }
188 | // //}
189 |
190 | // #endregion
191 |
192 | // #region [私有][基础]对选项值的操作【类型转换】【不支持List】
193 | // //获取选项值。不区分大小写。同时进行转换。同时使用ConvertValue()转换
194 | // //选项存在返回True;不存在返回False
195 | // //不存在时结果为null
196 | // bool _TryGetValue(string option, out object result, Type type)
197 | // {
198 | // if (_TryGetValue(option, out result))
199 | // {
200 | // result = (object)ConvertValue((string)result, type);
201 | // return true;
202 | // }
203 | // return false;
204 | // }
205 | // bool _TryGetValue(string option, out object result)
206 | // {
207 | // foreach (var pair in Options)
208 | // {
209 | // if (String.Compare(pair.Key, option, true) == 0)
210 | // {
211 | // result = pair.Value;
212 | // return true;
213 | // }
214 | // }
215 | // result = null;
216 | // return false;
217 | // }
218 |
219 |
220 | // //设置选项值。不区分大小写。同时使用ConvertBackValue()转换
221 | // //若值为null则删除选项。
222 | // //始终返回True
223 | // bool _TrySetValue(string option, object value)
224 | // {
225 | // foreach (var key in Options.Keys)
226 | // {
227 | // if (String.Compare(key, option, true) == 0)
228 | // {
229 | // if (value == null)
230 | // {
231 | // Options.Remove(key);
232 | // //删除注释
233 | // }
234 | // else
235 | // {
236 | // Options[key] = ConvertBackValue(value);
237 | // }
238 | // return true;
239 | // }
240 | // }
241 | // if (value != null)
242 | // Options.Add(option, ConvertBackValue(value));
243 |
244 | // return true;
245 | // }
246 |
247 | // #endregion
248 |
249 | // #region [动态]对成员的动态访问【类型转换】【List】
250 |
251 | // ///
252 | // /// 获取成员。不区分大小写。只允许值类型和List(string)
253 | // ///
254 | // ///
255 | // /// 若成员不存在,则返回null。(List除外)
256 | // /// 始终返回真
257 | // public override bool TryGetMember(GetMemberBinder binder, out object result)
258 | // {
259 | // return TryGetMember(binder.Name, binder.ReturnType, out result);
260 | // }
261 | // public bool TryGetMember(string name, Type type, out object result)
262 | // {
263 | // if (type == typeof(List))
264 | // {
265 | // result = getListValue(name);
266 | // return true;
267 | // }
268 | // _TryGetValue(name, out result, type);
269 | // return true;
270 | // }
271 |
272 | // ///
273 | // /// 设置成员。若成员不存在,则创建成员。不区分大小写。只允许值类型和List(string)
274 | // ///
275 | // /// 若值为null,则删除成员。(List除外)
276 | // /// 始终返回真
277 | // public override bool TrySetMember(SetMemberBinder binder, object value)
278 | // {
279 | // if (value.GetType() == typeof(List))
280 | // {
281 | // setListValue(binder.Name, (List)value);
282 | // return true;
283 | // }
284 | // _TrySetValue(binder.Name, value);
285 | // return true;
286 | // }
287 |
288 | // #endregion
289 |
290 | // #region [索引]通过索引访问【Get不转换】【Set转换】【不支持List】
291 | // ///
292 | // /// 获取或设置指定指定的选项的选项值。选项名称不区分大小写。
293 | // ///
294 | // /// 选项的名称
295 | // /// 指定的选项。如果找不到指定选项,get 将返回null,set 将创建指定值的选项
296 | // public string this[string option]
297 | // {
298 | // get
299 | // {
300 | // object result;
301 | // if (_TryGetValue(option, out result))
302 | // return result.ToString();
303 | // return null;
304 |
305 | // }
306 | // set
307 | // {
308 | // _TrySetValue(option, value);
309 | // }
310 | // }
311 |
312 | // #endregion
313 |
314 | // #region 对象转换与序列化
315 |
316 | // //Section转换为对象(dynamic Section 且 显式转换)对象只能是string或class
317 | // public override bool TryConvert(ConvertBinder binder, out object result)
318 | // {
319 | // if (binder.Type == typeof(string))
320 | // {
321 | // result = ToString();
322 | // return true;
323 | // }
324 | // else if (binder.Explicit && binder.Type.IsClass)
325 | // {
326 | // //只序列化可写的公共属性
327 | // result = DeserializeObject(binder.Type);
328 | // return true;
329 | // }
330 | // return base.TryConvert(binder, out result);
331 | // }
332 | // //Section -> Object
333 | // public T DeserializeObject()
334 | // {
335 | // return (T)DeserializeObject(typeof(T));
336 | // }
337 | // //Object -> Section
338 | // public void SerializeObject(T obj)
339 | // {
340 | // SerializeObject(obj, typeof(T));
341 | // }
342 |
343 |
344 | // //对象序列化到Section,【公有属性】【值类型】【List】
345 | // void SerializeObject(object obj, Type type)
346 | // {
347 | // foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
348 | // .Where(p => p.CanRead))
349 | // {
350 | // if (pi.PropertyType == typeof(List))
351 | // {
352 | // setListValue(pi.Name, (List)pi.GetValue(obj));
353 | // }
354 | // else //if (pi.PropertyType.IsValueType)
355 | // {
356 | // _TrySetValue(pi.Name, pi.GetValue(obj));
357 | // }
358 |
359 | // //检查注释
360 | // foreach (Attribute attr in Attribute.GetCustomAttributes(pi))
361 | // {
362 | // if (attr.GetType() == typeof(ExportMetadataAttribute))
363 | // {
364 | // var meta = (ExportMetadataAttribute)attr;
365 | // if (meta.Name.ToLower() == "description"
366 | // || meta.Name.ToLower() == "comment")
367 | // {
368 | // SetComment(pi.Name, "; " + meta.Value.ToString());
369 | // }
370 | // }
371 | // }
372 | // }
373 | // }
374 |
375 | // //从Section反序列化为对象【公有属性】【值类型】【List】
376 | // object DeserializeObject(Type targetType)
377 | // {
378 | // var ret = Activator.CreateInstance(targetType);
379 | // foreach (var pi in targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
380 | // .Where(p => p.CanWrite))
381 | // {
382 | // if (pi.PropertyType == typeof(List))
383 | // {
384 | // pi.SetValue(ret, (getListValue(pi.Name)));
385 | // }
386 | // else //if (pi.PropertyType.IsValueType)
387 | // {
388 | // object optValue;
389 | // if (_TryGetValue(pi.Name, out optValue, pi.PropertyType))
390 | // {
391 | // pi.SetValue(ret, optValue);
392 | // } //若没有该选项,则不赋值
393 | // }
394 | // }
395 | // return ret;
396 | // }
397 |
398 | // #endregion
399 |
400 | // #region List的处理
401 |
402 | // //返回option, option2, option3的列表选项值
403 | // List getListValue(string option)
404 | // {
405 | // var ret = new List();
406 | // object val;
407 | // string key = option;
408 | // int idx = 1;
409 | // while (_TryGetValue(key, out val))
410 | // {
411 | // ret.Add(ConvertValue((string)val, typeof(T)));
412 | // idx++;
413 | // key = option + idx;
414 | // }
415 | // return ret;
416 | // }
417 |
418 | // //从列表设置选项
419 | // void setListValue(string option, List list)
420 | // {
421 | // int idx = 1;
422 | // string key = option;
423 | // foreach (var value in list)
424 | // {
425 | // //_TrySetValue(key, ConvertBackValue(value));
426 | // //_TrySetValue已经使用了ConvertBackValue
427 | // _TrySetValue(key, value);
428 | // idx++;
429 | // key = option + idx;
430 | // }
431 | // }
432 |
433 | // #endregion
434 |
435 | // #region [基础][静态]值类型转换
436 | // //选项值类型转换。 (string -> ?)
437 | // static dynamic ConvertValue(string value, Type type)
438 | // {
439 | // if (type == typeof(string))
440 | // return value;
441 |
442 | // else if (type == typeof(int))
443 | // return ConvertInt(value);
444 |
445 | // else if (type == typeof(bool))
446 | // return ConvertBool(value);
447 |
448 | // return Convert.ChangeType(value, type);
449 | // }
450 |
451 | // //转换为选项值类型(? -> string)
452 | // static string ConvertBackValue(object value)
453 | // {
454 | // if (value.GetType() == typeof(bool))
455 | // return (bool)value ? "1" : "0";
456 | // return value.ToString();
457 | // }
458 |
459 | // //将值转换为Int32
460 | // static int ConvertInt(string value)
461 | // {
462 | // int ret;
463 | // if (Int32.TryParse(value, out ret))
464 | // return ret;
465 | // return default(int);
466 | // }
467 |
468 | // //将值转换为Boolean
469 | // static bool ConvertBool(string value)
470 | // {
471 | // if (String.Compare(value, "True", true) == 0) return true;
472 | // if (String.Compare(value, "False", true) == 0) return true;
473 | // int ret;
474 | // if (Int32.TryParse(value, out ret))
475 | // return ret > 0;
476 | // return default(bool);
477 | // }
478 |
479 | // #endregion
480 |
481 |
482 | // ///
483 | // /// 添加指定选项后的注释。空值选项表示节点注释。注释将自动换行。
484 | // ///
485 | // /// 选项名
486 | // /// 注释
487 | // public void AddComment(string option, string comment)
488 | // {
489 | // foreach (var key in Comments.Keys)
490 | // {
491 | // if (String.Compare(key, option, true) == 0)
492 | // {
493 | // Comments[key] += "\r\n" + comment;
494 | // return;
495 | // }
496 | // }
497 | // Comments.Add(option.ToLower(), comment);
498 | // }
499 | // public void SetComment(string option, string comment)
500 | // {
501 | // Comments[option.ToLower()] = comment;
502 | // }
503 |
504 |
505 |
506 | // ///
507 | // /// 将节点内容序列化为文本
508 | // ///
509 | // /// 表示节点内容的文本
510 | // public override string ToString()
511 | // {
512 | // StringBuilder contents = new StringBuilder();
513 |
514 | // //节点注释
515 | // if (Comments.ContainsKey(""))
516 | // {
517 | // contents.AppendLine(Comments[""]);
518 | // }
519 |
520 | // foreach (var pairs in Options)
521 | // {
522 | // contents.AppendFormat(@"{0}={1}", pairs.Key, pairs.Value);
523 | // contents.AppendLine();
524 |
525 | // var key = pairs.Key.ToLower();
526 | // if (Comments.ContainsKey(key))
527 | // {
528 | // contents.AppendLine(Comments[key]);
529 | // }
530 | // }
531 | // return contents.ToString(0, contents.Length - 2);
532 | // }
533 |
534 | // #region 枚举访问
535 |
536 | // public IEnumerator> GetEnumerator()
537 | // {
538 | // return Options.GetEnumerator();
539 | // }
540 |
541 | // //System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
542 | // //{
543 | // // return Options.GetEnumerator();
544 | // //}
545 | // #endregion 枚举访问
546 | //}
547 |
548 | #endregion
549 |
550 |
551 |
552 | ///
553 | /// 表示一个INI配置文件
554 | ///
555 | public class ConfigParser
556 | {
557 | ///
558 | /// 此INI文件的文档注释。
559 | /// 文档注释位于所有节点的上面。
560 | ///
561 | public string Comment { set; get; }
562 |
563 | Dictionary m_Sections;
564 |
565 | ///
566 | /// 获取或设置指定名称的节点。节点名不区分大小写。
567 | ///
568 | /// 节点的名称
569 | /// 指定的节点。如果找不到指定的节点,get 将返回null,set 将创建指定的节点
570 | public Section this[string section]
571 | {
572 | set
573 | {
574 | m_Sections[section] = value;
575 | }
576 | get
577 | {
578 | foreach (var key in m_Sections.Keys)
579 | {
580 | if (String.Compare(key, section, true) == 0)
581 | return m_Sections[key];
582 | }
583 | return null;
584 | }
585 | }
586 |
587 | ///
588 | /// 获取或设置指定节点的指定选项的值。节点名与选项名不区分大小写。
589 | ///
590 | /// 节点的名称
591 | /// 选项的名称
592 | /// 指定选项值。如果找不到指定的节点或选项,get 将返回null,set 将创建节点或选项
593 | public string this[string section, string option]
594 | {
595 | set
596 | {
597 | var sec = this[section] = this[section] ?? new Section();
598 | sec[option] = value;
599 | }
600 | get
601 | {
602 | var sec = this[section];
603 | if (sec != null)
604 | return sec[option];
605 | return null;
606 | }
607 | }
608 |
609 | ///
610 | /// Variables 节点。
611 | /// 声明了在INI文档中可以通过“#变量名#”的形式引用的名为“变量”的特使选项。
612 | /// 此成员仅用于替换变量的引用,不会被直接打印。
613 | ///
614 | public Section Variables { private set; get; }
615 |
616 | //BuildInVariables
617 |
618 |
619 | ///
620 | /// 初始化类的空白新实例。
621 | /// 也可以使用的静态方法来创建新实例。
622 | ///
623 | public ConfigParser()
624 | {
625 | m_Sections = new Dictionary();
626 | Variables = new Section();
627 | }
628 |
629 | ///
630 | /// 由一个INI文件创建类的新实例。
631 | /// 若文件不存在或无法访问将引发异常。
632 | ///
633 | /// 要加载的INI文件地址
634 | /// 应用到文件内容的字符编码
635 | /// INI文件不存在
636 | public static ConfigParser ReadIniFile(string iniFile, Encoding encoding = null)
637 | {
638 | //检查文件是否存在
639 | if (!File.Exists(iniFile))
640 | {
641 | throw new FileNotFoundException("指定的INI文件不存在。", iniFile);
642 | }
643 |
644 | ConfigParser parser = new ConfigParser();
645 |
646 | //parser.Parse(File.ReadAllText(iniFile, encoding ?? Encoding.Default));
647 | parser.Parse(File.ReadAllLines(iniFile, encoding ?? Encoding.Default));
648 |
649 | //设置文档变量【读取文档前设置的变量会丢失
650 | parser._ResetVariablesProperty();
651 |
652 | return parser;
653 | }
654 |
655 | #region 解析文本并添加到节点中
656 |
657 | //[基础]将一行文本解析并添加到指定节点中
658 | string ParseLine(string line, ref Section section, string lastOption)
659 | {
660 | line = line.Trim();
661 | int pos = line.IndexOf('=');
662 |
663 | //当前行是 Option=Value
664 | if (pos > 0)
665 | {
666 | string option = line.Substring(0, pos).TrimEnd();
667 | string value = line.Substring(pos + 1).TrimStart();
668 | section[option] = value;
669 | return option;
670 | }
671 | //当前行是 无效内容
672 | else
673 | {
674 | section.AddComment(lastOption, line);
675 | return lastOption;
676 | }
677 | }
678 |
679 | ///
680 | /// 将一段只包含一个节点内容的文本解析并添加到指定节点。(针对 单节点文档)
681 | ///
682 | /// 包含节点内容的文本,不含节点标签
683 | /// 节点名
684 | public void Parse(string content, string section)
685 | {
686 | var curSection = this[section] = this[section] ?? new Section();
687 | string[] lines = content.Split('\n');
688 | Parse(lines, curSection, "");
689 | }
690 |
691 | ///
692 | /// 将一段包含多个节点内容的文本解析并添加到相应节点。(针对 多节点文档)
693 | ///
694 | /// 包含多个节点内容的文本
695 | public void Parse(string content)
696 | {
697 | string[] lines = content.Split('\n');
698 | Parse(lines, null, "");
699 | }
700 |
701 | public void Parse(string[] lines)
702 | {
703 | Parse(lines, null, "");
704 | }
705 |
706 | //[基础]解析多行
707 | void Parse(string[] lines, Section curSection, string curOption)
708 | {
709 | foreach (var li in lines)
710 | {
711 | var line = li.Trim();
712 | //当前行是 [节点] 标记
713 | if (line.StartsWith("[") && line.EndsWith("]"))
714 | {
715 | var section = line.Substring(1, line.Count() - 2);
716 |
717 | curSection = this[section] = this[section] ?? new Section();
718 | curOption = "";
719 | }
720 | else
721 | {
722 | //当前行位于 任何节点以外
723 | if (curSection == null)
724 | {
725 | if (String.IsNullOrEmpty(Comment))
726 | Comment = line;
727 | else
728 | Comment += "\r\n" + line;
729 | }
730 | //当前行位于 某个节点下
731 | else
732 | {
733 | curOption = ParseLine(line, ref curSection, curOption);
734 | }
735 | }
736 | }
737 | }
738 |
739 | #endregion
740 |
741 | #region 输出与保存
742 |
743 | ///
744 | /// 将INI文档保存并输出。
745 | /// 若文件无法访问将引发异常。
746 | ///
747 | /// INI文件地址
748 | /// 应用到文件的字符编码
749 | /// 必须是已经存在的文件夹
750 | public void SaveIniFile(string iniFile, Encoding encoding = null)
751 | {
752 | File.WriteAllText(iniFile, Print(), Encoding.Default);
753 | }
754 |
755 | ///
756 | /// 尝试将INI文档保存并输出。返回是否成功。
757 | ///
758 | /// INI文件地址
759 | /// 应用到文件的字符编码
760 | /// 是否成功
761 | public bool TrySaveIniFile(string iniFile, Encoding encoding = null)
762 | {
763 | try { SaveIniFile(iniFile, encoding); }
764 | catch { return false; }
765 | return true;
766 | }
767 |
768 | ///
769 | /// 将INI文档内容序列化为文本。
770 | ///
771 | /// 表示文档内容的文本
772 | public string Print()
773 | {
774 | StringBuilder contents = new StringBuilder();
775 |
776 | //节点注释
777 | if (!String.IsNullOrEmpty(Comment))
778 | {
779 | contents.AppendLine(Comment);
780 | }
781 |
782 | foreach (var pairs in m_Sections)
783 | {
784 | contents.AppendFormat(
785 | @"[{0}]
786 | {1}
787 | ", pairs.Key, pairs.Value.ToString());
788 | }
789 |
790 | return contents.ToString();
791 | }
792 |
793 | #endregion
794 |
795 | #region 文档变量的读取与替换
796 |
797 | ///
798 | /// 将重新设置为["Variables"]。
799 | /// 是非公开写入的,以避免被设置为了null。
800 | /// 此方法应当仅被用于方法中。
801 | ///
802 | public void _ResetVariablesProperty()
803 | {
804 | var _vars = this["Variables"];
805 | if (_vars != null)
806 | this.Variables = _vars;
807 | }
808 |
809 | ///
810 | /// 获取变量。推荐直接使用 [name] ?? defValue
811 | ///
812 | /// 变量名
813 | /// 默认值
814 | ///
815 | public string GetVariable(string name,string defValue)
816 | {
817 | //if (Variables == null) return defValue;
818 | //变量应当可以是空白的,在使用#var#引用时替换为""!
819 | //这点与Rainmeter不同
820 | return Variables[name] ?? defValue;
821 | }
822 |
823 | ///
824 | /// 替换一段字符串中的所有变量,并返回结果
825 | ///
826 | /// 包含变量的字符串
827 | /// 替换后的字符串
828 | public string ReplaceVariables(string str)
829 | {
830 | //if (Variables == null) return str;
831 |
832 | if (String.IsNullOrEmpty(str)) return "";
833 | var spls = str.Split('#');
834 | var lst = spls.Count() - 1;
835 | bool isvar = false; //当前字符串是否为变量
836 | for (int i = 0; i < lst; i++)
837 | {
838 | if (isvar)
839 | {
840 | var _key = spls[i].Trim();
841 | var _val = "";
842 | isvar = !String.IsNullOrEmpty(_key);
843 | if (isvar)
844 | {
845 | _val = Variables[_key];
846 | isvar = _val != null; //变量可以是空的
847 | }
848 | if (isvar)
849 | spls[i] = _val;
850 | else
851 | spls[i] = "#" + spls[i];
852 | }
853 | isvar = !isvar;
854 | }
855 | if (isvar) spls[lst] = "#" + spls[lst];
856 | return String.Concat(spls);
857 | }
858 |
859 | #endregion
860 |
861 | #region 读取指定选项的值
862 |
863 | ///
864 | /// 读取值类型为的选项。当节点或选项不存在时,返回指定的默认值。
865 | ///
866 | /// 节点名
867 | /// 选项名
868 | /// 默认值
869 | /// 是否进行变量的替换
870 | /// 选项值
871 | public string ReadString(string section, string option, string defValue = "", bool replaceVar = false)
872 | {
873 | var sec = this[section];
874 | if (sec != null)
875 | {
876 | defValue = sec[option] ?? defValue;
877 | }
878 | if (replaceVar) return ReplaceVariables(defValue);
879 |
880 | return defValue;
881 | }
882 |
883 | ///
884 | /// 读取值类型为的选项。当节点或选项不存在时,返回指定的默认值。
885 | ///
886 | /// 节点名
887 | /// 选项名
888 | /// 默认值
889 | /// 选项值
890 | public int ReadInt(string section, string option, int defValue = 0)
891 | {
892 | var ret = ReadString(section, option, "");
893 |
894 | int value;
895 | if (ret != "" && Int32.TryParse(ret, out value))
896 | return value;
897 |
898 | return defValue;
899 | }
900 |
901 | ///
902 | /// 读取值类型为的选项。当节点或选项不存在时,返回指定的默认值。
903 | ///
904 | /// 节点名
905 | /// 选项名
906 | /// 默认值
907 | /// 选项值
908 | public bool ReadBool(string section,string option, bool defValue = false)
909 | {
910 | return ReadInt(section, option, defValue ? 1 : 0) > 0 ;
911 | }
912 |
913 | ///
914 | /// 读取像, 2, 3这样的连续的选项
915 | ///
916 | /// 节点名
917 | /// 选项名
918 | /// 最大读取数量(-1代表无限循环)
919 | ///
920 | public List ReadList(string section, string option)
921 | {
922 | var sec = this[section];
923 | if (sec == null) return new List();
924 |
925 | return getListValue(option, sec);
926 | }
927 |
928 | #endregion
929 |
930 | #region 设置指定选项值以及注释
931 | ///
932 | /// 设置指定节点下指定选项的值。操作将自动创建不存在的节点和选项。
933 | // 推荐直接使用来设置选项,因为后者会自动进行值类型的转换。
934 | ///
935 | /// 节点名
936 | /// 选项名
937 | /// 选项值
938 | public void SetValue(string section, string option, object value)
939 | {
940 | var sec = this[section];
941 | if (sec == null)
942 | this[section] = sec = new Section();
943 |
944 | sec[option] = ConvertBackValue(value);
945 | }
946 | void SetValue(Section section,string option, object value)
947 | {
948 | section[option] = ConvertBackValue(value);
949 | }
950 | public void SetList(string section, string option, IEnumerable list)
951 | {
952 | var sec = this[section];
953 | if (sec == null)
954 | this[section] = sec = new Section();
955 |
956 | setListValue(option, list, sec);
957 | }
958 |
959 |
960 | ///
961 | /// 添加指定节点下指定选项后的注释。不应当对注释有积极的操作。
962 | ///
963 | /// 节点名。将忽略不存在的节点。
964 | /// 选项名。空值表示节点的注释。
965 | /// 注释。将自动换行。
966 | public void AddComment(string section, string option, string comment)
967 | {
968 | var sec = this[section];
969 | if ( sec != null)
970 | sec.AddComment(option, comment);
971 | }
972 |
973 | #endregion
974 |
975 | #region List的处理
976 |
977 | //返回option, option2, option3的列表选项值
978 | List getListValue(string option, Section section)
979 | {
980 | var ret = new List();
981 | string key = option;
982 | string val = section[option];
983 | int idx = 1;
984 | while (val != null)
985 | {
986 | ret.Add(ConvertValue(val, typeof(T)));
987 | idx++;
988 | key = option + idx;
989 | }
990 | return ret;
991 | }
992 |
993 | //从列表设置选项
994 | void setListValue(string option, IEnumerable list, Section section)
995 | {
996 | int idx = 1;
997 | string key = option;
998 | foreach (var value in list)
999 | {
1000 | section[key] = ConvertBackValue(value);
1001 | idx++;
1002 | key = option + idx;
1003 | }
1004 | }
1005 |
1006 | #endregion
1007 |
1008 | #region [静态]值类型转换
1009 | //选项值类型转换。 (string -> ?)
1010 | static dynamic ConvertValue(string value, Type type)
1011 | {
1012 | if (type == typeof(string))
1013 | return value;
1014 |
1015 | else if (type == typeof(int))
1016 | return ConvertInt(value);
1017 |
1018 | else if (type == typeof(bool))
1019 | return ConvertBool(value);
1020 |
1021 | return Convert.ChangeType(value, type);
1022 | }
1023 |
1024 | //转换为选项值类型(? -> string)
1025 | static string ConvertBackValue(object value)
1026 | {
1027 | if (value.GetType() == typeof(bool))
1028 | return (bool)value ? "1" : "0";
1029 | return value.ToString();
1030 | }
1031 |
1032 | //将值转换为Int32
1033 | static int ConvertInt(string value)
1034 | {
1035 | int ret;
1036 | if (Int32.TryParse(value, out ret))
1037 | return ret;
1038 | return default(int);
1039 | }
1040 |
1041 | //将值转换为Boolean
1042 | static bool ConvertBool(string value)
1043 | {
1044 | if (String.Compare(value, "True", true) == 0) return true;
1045 | if (String.Compare(value, "False", true) == 0) return false;
1046 | int ret;
1047 | if (Int32.TryParse(value, out ret))
1048 | return ret > 0;
1049 | return default(bool);
1050 | }
1051 |
1052 | #endregion
1053 |
1054 | #region 对象转换与序列化
1055 |
1056 |
1057 | ///
1058 | /// Section -> Object
1059 | ///
1060 | ///
1061 | ///
1062 | ///
1063 | public T DeserializeObject(string section)where T : new()
1064 | {
1065 | Section sec = this[section];
1066 | T ret = new T();
1067 |
1068 | if (sec != null)
1069 | DeserializeObject(ret, typeof(T), sec);
1070 |
1071 | return ret;
1072 | }
1073 | ///
1074 | /// Object -> Section
1075 | ///
1076 | ///
1077 | ///
1078 | ///
1079 | public void SerializeObject(T obj, string section)
1080 | {
1081 | Section sec = this[section] = this[section] ?? new Section();
1082 | SerializeObject(obj, typeof(T), ref sec);
1083 | }
1084 |
1085 |
1086 | //对象序列化到Section,【公有属性】【值类型】【List】
1087 | void SerializeObject(object obj, Type type, ref Section section)
1088 | {
1089 | foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
1090 | .Where(p => p.CanRead))
1091 | {
1092 | if (pi.PropertyType == typeof(List))
1093 | {
1094 | setListValue(pi.Name, (List)pi.GetValue(obj), section);
1095 | }
1096 | else //if (pi.PropertyType.IsValueType)
1097 | {
1098 | section[pi.Name] = ConvertBackValue(pi.GetValue(obj));
1099 | }
1100 |
1101 | //检查注释
1102 | foreach (Attribute attr in Attribute.GetCustomAttributes(pi))
1103 | {
1104 | if (attr.GetType() == typeof(ExportMetadataAttribute))
1105 | {
1106 | var meta = (ExportMetadataAttribute)attr;
1107 | if (meta.Name.ToLower() == "description"
1108 | || meta.Name.ToLower() == "comment")
1109 | {
1110 | string buf = meta.Value.ToString();
1111 | if (!buf.StartsWith(";")) buf = "; " + buf;
1112 | section.SetComment(pi.Name, buf);
1113 | }
1114 | }
1115 | }
1116 | }
1117 | }
1118 |
1119 | //从Section反序列化为对象【公有属性】【值类型】【List】
1120 | void DeserializeObject(object obj, Type targetType, Section section)
1121 | {
1122 | foreach (var pi in targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
1123 | .Where(p => p.CanWrite))
1124 | {
1125 | if (pi.PropertyType == typeof(List))
1126 | {
1127 | pi.SetValue(obj, (getListValue(pi.Name,section)));
1128 | }
1129 | else //if (pi.PropertyType.IsValueType)
1130 | {
1131 | string optValue = section[pi.Name];
1132 | if (optValue != null)
1133 | {
1134 | pi.SetValue(obj, ConvertValue(optValue,pi.PropertyType));
1135 | } //若没有该选项,则不赋值
1136 | }
1137 | }
1138 | }
1139 | #endregion
1140 |
1141 | #region 枚举访问
1142 |
1143 | public IEnumerator> GetEnumerator()
1144 | {
1145 | return m_Sections.GetEnumerator();
1146 | }
1147 |
1148 | #endregion 枚举访问
1149 | }
1150 | }
1151 |
1152 |
--------------------------------------------------------------------------------
/KanColleCacher/Extension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace d_f_32.KanColleCacher
8 | {
9 | public static class Extension
10 | {
11 | public static bool IsFilePath(this Uri uri)
12 | {
13 | return uri.Segments.Last().IndexOf('.') >= 0;
14 | }
15 | //AbsoluteUrl = http://www.contoso.com/catalog/shownew.htm?date=today
16 | //AbsolutePath = /catalog/shownew.htm
17 | //PathAndQuery = /catalog/shownew.htm?date=today
18 |
19 | //Segments = (string[]) {/, catalog/, shownew.htm} (绝对路径的片段)
20 | //Fragment 返回#后的内容(书签,#不含)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/KanColleCacher/FiddlerRules.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Fiddler;
8 | using Debug = System.Diagnostics.Debug;
9 |
10 | namespace d_f_32.KanColleCacher
11 | {
12 | enum Direction
13 | {
14 | Discharge_Response, //无关请求,或需要下载文件 -> 忽略请求
15 | Return_LocalFile, //存在无需验证的本地文件 -> 返回本地文件
16 | Verify_LocalFile, //验证文件有效性 -> 向服务器发送验证请求
17 | }
18 |
19 | class FiddlerRules
20 | {
21 | static CacheCore cache;
22 |
23 | static public void Initialize ()
24 | {
25 | cache = new CacheCore();
26 |
27 | _AppendToFiddler();
28 | }
29 |
30 | static void _AppendToFiddler()
31 | {
32 | FiddlerApplication.BeforeRequest += _BeforeRequest;
33 | FiddlerApplication.BeforeResponse += _BeforeResponse;
34 | FiddlerApplication.AfterSessionComplete += _AfterComplete;
35 | }
36 |
37 | static bool _Filter(Session oSession)
38 | {
39 | return oSession.PathAndQuery.StartsWith("/kcs/");
40 | }
41 |
42 | #region Fiddler向服务器发送客户端请求前执行的动作
43 | //This event fires when a client request is received by Fiddler
44 | static void _BeforeRequest(Session oSession)
45 | {
46 | if (!Settings.Current.CacheEnabled) return;
47 | if (!_Filter(oSession)) return;
48 |
49 | string filepath;
50 | var direction = cache.GotNewRequest(oSession.fullUrl, out filepath);
51 |
52 | if (direction == Direction.Return_LocalFile)
53 | {
54 | //返回本地文件
55 | oSession.utilCreateResponseAndBypassServer();
56 | oSession.ResponseBody = File.ReadAllBytes(filepath);
57 | _CreateResponseHeader(oSession, filepath);
58 |
59 | //Debug.WriteLine("CACHR> 【返回本地】" + filepath);
60 | }
61 | else if (direction == Direction.Verify_LocalFile)
62 | {
63 | //请求服务器验证文件
64 | //oSession.oRequest.headers["If-Modified-Since"] = result;
65 | oSession.oRequest.headers["If-Modified-Since"] = _GetModifiedTime(filepath);
66 | oSession.bBufferResponse = true;
67 |
68 | //Debug.WriteLine("CACHR> 【验证文件】" + oSession.PathAndQuery);
69 | }
70 | else
71 | {
72 | //下载文件
73 | }
74 | }
75 | #endregion
76 |
77 | #region Fiddler向客户端返回服务器响应前执行的动作
78 | //This event fires when a server response is received by Fiddler
79 | static void _BeforeResponse(Session oSession)
80 | {
81 | if (!_Filter(oSession)) return;
82 | if (oSession.responseCode == 304)
83 | {
84 | string filepath = TaskRecord.GetAndRemove(oSession.fullUrl);
85 | //只有TaskRecord中有记录的文件才是验证的文件,才需要修改Header
86 | if (!string.IsNullOrEmpty(filepath))
87 | {
88 | //服务器返回304,文件没有修改 -> 返回本地文件
89 | oSession.bBufferResponse = true;
90 | oSession.ResponseBody = File.ReadAllBytes(filepath);
91 | oSession.oResponse.headers.HTTPResponseCode = 200;
92 | oSession.oResponse.headers.HTTPResponseStatus = "200 OK";
93 | oSession.oResponse.headers["Last-Modified"] = oSession.oRequest.headers["If-Modified-Since"];
94 | oSession.oResponse.headers["Accept-Ranges"] = "bytes";
95 | oSession.oResponse.headers.Remove("If-Modified-Since");
96 | oSession.oRequest.headers.Remove("If-Modified-Since");
97 | if (filepath.EndsWith(".swf"))
98 | oSession.oResponse.headers["Content-Type"] = "application/x-shockwave-flash";
99 |
100 | //Debug.WriteLine(oSession.oResponse.headers.ToString());
101 | //Debug.WriteLine("");
102 | //Debug.WriteLine("CACHR> 【捕获 304】" + oSession.PathAndQuery);
103 | //Debug.WriteLine(" " + filepath);
104 | }
105 | }
106 | }
107 | #endregion
108 |
109 | #region 会话完成时执行的动作
110 | static void _AfterComplete(Session oSession)
111 | {
112 | if (!Settings.Current.CacheEnabled) return;
113 | if (!_Filter(oSession)) return;
114 |
115 | //服务器返回200,下载新的文件
116 | if (oSession.responseCode == 200)
117 | {
118 | string filepath = TaskRecord.GetAndRemove(oSession.fullUrl);
119 |
120 | //只有TaskRecord中有记录的文件才是验证的文件,才需要保存
121 | if (!string.IsNullOrEmpty(filepath))
122 | {
123 | if (File.Exists(filepath))
124 | File.Delete(filepath);
125 |
126 | //保存下载文件并记录Modified-Time
127 | try
128 | {
129 | oSession.SaveResponseBody(filepath);
130 | //cache.RecordNewModifiedTime(oSession.fullUrl,
131 | // oSession.oResponse.headers["Last-Modified"]);
132 | _SaveModifiedTime(filepath, oSession.oResponse.headers["Last-Modified"]);
133 | //Debug.WriteLine("CACHR> 【下载文件】" + oSession.PathAndQuery);
134 | }
135 | catch (Exception ex)
136 | {
137 | Log.Exception(oSession, ex, "会话结束时,保存返回文件时发生异常");
138 | }
139 | }
140 | }
141 | }
142 | #endregion
143 |
144 | static void _CreateResponseHeader(Session oSession, string filename)
145 | {
146 | oSession.oResponse.headers["Server"] = "Apache";
147 | oSession.oResponse.headers["Cache-Control"] = "max-age=18000, public";
148 | oSession.oResponse.headers["Date"] = GMTHelper.ToGMTString(DateTime.Now);
149 | oSession.oResponse.headers["Connection"] = "close";
150 | oSession.oResponse.headers["Accept-Ranges"] = "bytes";
151 | filename.ToLower();
152 | if (filename.EndsWith(".swf"))
153 | oSession.oResponse.headers["Content-Type"] = "application/x-shockwave-flash";
154 | else if (filename.EndsWith(".mp3"))
155 | oSession.oResponse.headers["Content-Type"] = "audio/mpeg";
156 | else if (filename.EndsWith(".png"))
157 | oSession.oResponse.headers["Content-Type"] = "image/png";
158 |
159 | }
160 |
161 |
162 |
163 |
164 | static void _SaveModifiedTime(string filepath, string gmTime)
165 | {
166 | FileInfo fi;
167 | try
168 | {
169 | fi = new FileInfo(filepath);
170 | fi.LastWriteTime = GMTHelper.GMT2Local(gmTime);
171 | }
172 | catch (Exception ex)
173 | {
174 | Log.Exception("FiddlerRules", ex, "在保存文件修改时间时发生异常。");
175 | }
176 | }
177 |
178 | static string _GetModifiedTime(string filepath)
179 | {
180 | FileInfo fi;
181 | try
182 | {
183 | fi = new FileInfo(filepath);
184 | return GMTHelper.ToGMTString(fi.LastWriteTime);
185 | }
186 | catch (Exception ex)
187 | {
188 | Log.Exception("FiddlerRules", ex, "在保存文件修改时间时发生异常。");
189 | return "";
190 | }
191 | }
192 |
193 | #region 保留的代码
194 | //
195 | //_BeforeResponse = {
196 | // oSession.utilDecodeResponse();
197 | // oSession.ResponseBody = File.ReadAllBytes(filename);
198 | // oSession.oResponse.headers.HTTPResponseCode = 200;
199 | // oSession.oResponse.headers.HTTPResponseStatus = "200 OK";
200 | // }
201 | //
202 | //_BeforeRequest = {
203 | // oSession.bBufferResponse = true;
204 | // }
205 |
206 | #endregion
207 |
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/KanColleCacher/GMTHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace d_f_32.KanColleCacher
8 | {
9 | public class GMTHelper
10 | {
11 | ///
12 | /// 本地时间转成GMT时间
13 | ///
14 | /// Thu, 29 Sep 2011 15:04:39 GMT
15 | /// ToGMTString(DateTime.Now)
16 | public static string ToGMTString(DateTime dt)
17 | {
18 | return dt.ToUniversalTime().ToString("r");
19 | }
20 |
21 | #region 未引用的函数
22 |
23 | ///
24 | /// 本地时间转成GMT格式的时间
25 | ///
26 | /// Thu, 29 Sep 2011 15:04:39 GMT+0800
27 | public static string ToGMTFormat(DateTime dt)
28 | {
29 | return dt.ToString("r") + dt.ToString("zzz").Replace(":", "");
30 | }
31 |
32 | ///
33 | /// GMT时间转成本地时间
34 | ///
35 | /// Thu, 29 Sep 2011 07:04:39 GMT
36 | /// Thu, 29 Sep 2011 15:04:39 GMT+0800
37 | ///
38 | public static DateTime GMT2Local(string gmt)
39 | {
40 | DateTime dt = DateTime.MinValue;
41 | try
42 | {
43 | string pattern = "";
44 | if (gmt.IndexOf("+0") != -1)
45 | {
46 | gmt = gmt.Replace("GMT", "");
47 | pattern = "ddd, dd MMM yyyy HH':'mm':'ss zzz";
48 | }
49 | if (gmt.ToUpper().IndexOf("GMT") != -1)
50 | {
51 | pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
52 | }
53 | if (pattern != "")
54 | {
55 | dt = DateTime.ParseExact(gmt, pattern, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal);
56 | dt = dt.ToLocalTime();
57 | }
58 | else
59 | {
60 | dt = Convert.ToDateTime(gmt);
61 | }
62 | }
63 | catch
64 | {
65 | }
66 | return dt;
67 | }
68 |
69 | #endregion
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/KanColleCacher/GraphList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Grabacr07.KanColleWrapper;
7 | using Grabacr07.KanColleWrapper.Models;
8 | using Grabacr07.KanColleWrapper.Models.Raw;
9 | using Fiddler;
10 | using System.IO;
11 | using Debug = System.Diagnostics.Debug;
12 | using System.Runtime.Serialization.Json;
13 | using System.Windows;
14 |
15 |
16 | namespace d_f_32.KanColleCacher
17 | {
18 | class GraphList
19 | {
20 | static List graphList = new List();
21 | static Session api_start2;
22 |
23 | ///
24 | /// 将解析完成的信息保存到本地
25 | ///
26 | static void PrintToFile(string filepath)
27 | {
28 | StringBuilder content = new StringBuilder();
29 |
30 | content.AppendFormat(
31 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n",
32 | "SortNo", "ShipId", "ShipName",
33 | "FileName", "FileVersion",
34 | "TypeName", "TypeId"
35 | );
36 | content.AppendFormat(
37 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n",
38 | "序号", "ID", "名称",
39 | "文件名", "文件版本", "文件序号",
40 | "类型", "类型序号", "类型ID"
41 | );
42 | try
43 | {
44 | graphList.Sort((x, y) =>
45 | {
46 | if (x.ship_sortno == y.ship_sortno)
47 | {
48 | if (x.ship_id == y.ship_id)
49 | return 0;
50 |
51 | return x.ship_id < y.ship_id ? -1 : 1;
52 | }
53 |
54 | return x.ship_sortno < y.ship_sortno ? -1 : 1;
55 | });
56 | }
57 | catch (Exception ex)
58 | {
59 | Debug.WriteLine("Cachr> GraphList.PrintToFile() 排序时发生异常(graphList.Sort)");
60 | Debug.WriteLine(ex);
61 | }
62 |
63 |
64 | graphList.ForEach(x =>
65 | {
66 | content.AppendFormat(
67 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n",
68 | x.ship_sortno, x.ship_id, x.ship_name,
69 | x.ship_filename, x.ship_version,
70 | x.ship_type_name, x.ship_type_id
71 | );
72 | });
73 |
74 | try
75 | {
76 | File.WriteAllText(filepath, content.ToString());
77 | }
78 | catch (Exception ex)
79 | {
80 | Log.Exception(ex.Source, ex, "写入立绘列表文件时异常");
81 | }
82 | }
83 |
84 | ///
85 | /// 解析 api_start2 数据信息
86 | ///
87 | static void ParseSession(Session oSession)
88 | {
89 | SvData svd;
90 | if (!SvData.TryParse(oSession, out svd))
91 | {
92 | Log.Warning("GraphList.ParseSession()", "TryParse失败,无效的Session对象!");
93 | return;
94 | }
95 |
96 | var mst_shipgraph = svd.Data.api_mst_shipgraph
97 | .ToDictionary(x => x.api_id);
98 | var mst_ship = svd.Data.api_mst_ship
99 | .ToDictionary(x => x.api_id);
100 | var mst_stype = svd.Data.api_mst_stype
101 | .ToDictionary(x => x.api_id);
102 |
103 | graphList.Clear();
104 |
105 | foreach (var _pair in mst_shipgraph)
106 | {
107 | var item = new ship_graph_item();
108 | var _loc1 = _pair.Value;
109 |
110 | item.ship_id = _loc1.api_id;
111 | item.ship_filename = _loc1.api_filename;
112 | item.ship_version = _loc1.api_version;
113 | item.ship_graph_sortno = _loc1.api_sortno;
114 |
115 | if (mst_ship.ContainsKey(item.ship_id))
116 | {
117 | var _loc2 = mst_ship[item.ship_id];
118 |
119 | item.ship_sortno = _loc2.api_sortno;
120 | item.ship_name = _loc2.api_name;
121 | item.ship_type_id = _loc2.api_stype;
122 |
123 | if (mst_stype.ContainsKey(item.ship_type_id))
124 | {
125 | var _loc3 = mst_stype[item.ship_type_id];
126 | item.ship_type_name = _loc3.api_name;
127 | item.ship_type_sortno = _loc3.api_sortno;
128 | }
129 |
130 | graphList.Add(item);
131 | mst_ship.Remove(item.ship_id);
132 | }
133 | else
134 | {
135 | #if DEBUG
136 | Debug.WriteLine(@"CACHR> shipgraph->ship匹配失败
137 | > {0} = {1} {2} {3}
138 | ", _loc1.ToString(), _loc1.api_id, _loc1.api_sortno, _loc1.api_filename);
139 | #endif
140 | }
141 | }
142 |
143 | #if DEBUG
144 | Debug.WriteLine("CACHR> graphList = {0}, mst_shipgraph = {1}",
145 | graphList.Count.ToString(),
146 | mst_shipgraph.Count.ToString()
147 | );
148 | #endif
149 | }
150 |
151 | ///
152 | /// 开始生成 GraphList.txt 文件
153 | ///
154 | static public void GenerateList()
155 | {
156 |
157 | if (api_start2 == null)
158 | {
159 | MessageBox.Show("无法生成舰娘列表,因为没有保存 api_start2 通信数据。", "提督很忙!缓存工具");
160 | return;
161 | }
162 |
163 | try
164 | {
165 | ParseSession(api_start2);
166 | }
167 | catch (Exception ex)
168 | {
169 | MessageBox.Show("未能生成舰娘列表。解析 api_start2 数据时发生异常。", "提督很忙!缓存工具");
170 | Log.Exception(ex.Source, ex, "解析 api_start2 数据时发生异常。");
171 | return;
172 | }
173 | try
174 | {
175 | string filepath = Settings.Current.CacheFolder + "\\GraphList.txt";
176 | PrintToFile(filepath);
177 | var si = new System.Diagnostics.ProcessStartInfo()
178 | {
179 | FileName = filepath,
180 | UseShellExecute = true,
181 | };
182 | System.Diagnostics.Process.Start(si);
183 | }
184 | catch (Exception ex)
185 | {
186 | Log.Exception(ex.Source, ex, "写入GraphList.txt时或启动进程时发生异常");
187 | return;
188 | }
189 | }
190 |
191 | ///
192 | /// Fiddler规则(通信完成后
193 | ///
194 | static public void RulePrintGraphList(Session oSession)
195 | {
196 | if (oSession.PathAndQuery != "/kcsapi/api_start2")
197 | return;
198 |
199 | //Debug.WriteLine("【START2】" + oSession.PathAndQuery);
200 |
201 | api_start2 = oSession;
202 | //移除规则
203 | RemoveRule();
204 | }
205 |
206 | static public void AppendRule()
207 | {
208 | FiddlerApplication.AfterSessionComplete += RulePrintGraphList;
209 | //Debug.WriteLine("CACHR> RulePrintGraphList Appended");
210 | }
211 |
212 | static public void RemoveRule()
213 | {
214 | FiddlerApplication.AfterSessionComplete -= RulePrintGraphList;
215 | //Debug.WriteLine("CACHR> RulePrintGraphList Removed");
216 | }
217 | }
218 |
219 |
220 | class ship_graph_item
221 | {
222 | public int ship_id = 0;
223 | public int ship_sortno = 0;
224 | public string ship_name = "";
225 |
226 | public int ship_type_id = 0;
227 | public int ship_type_sortno = 0;
228 | public string ship_type_name = "";
229 |
230 | public int ship_graph_sortno = 0;
231 | public string ship_filename = "";
232 | public string ship_version = "";
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/KanColleCacher/KanColleCacher.cs:
--------------------------------------------------------------------------------
1 | using Grabacr07.KanColleViewer.Composition;
2 | using System.ComponentModel.Composition;
3 | using Debug = System.Diagnostics.Debug;
4 | using File = System.IO.File;
5 |
6 | namespace d_f_32.KanColleCacher
7 | {
8 | [Export(typeof(IToolPlugin))]
9 | [ExportMetadata("Title", AssemblyInfo.Name)]
10 | [ExportMetadata("Description", AssemblyInfo.Description)]
11 | [ExportMetadata("Version", AssemblyInfo.Version)]
12 | [ExportMetadata("Author", AssemblyInfo.Author)]
13 | public class KanColleCacher : IToolPlugin
14 | {
15 | const string name = "缓存工具";
16 | static bool isInitialized = false;
17 | static CacherToolView view;
18 |
19 | static public void Initialize()
20 | {
21 | if (isInitialized) return;
22 | isInitialized = true;
23 |
24 | #if DEBUG
25 | Debug.WriteLine(@"
26 | KanColleCacher
27 | =================================================
28 | CACHR> 初始化开始:{0}
29 | ", System.DateTime.Now);
30 | #endif
31 |
32 | Settings.Load();
33 | view = new CacherToolView();
34 |
35 | //Debug.WriteLine(@"CACHR> GraphList加入规则");
36 |
37 | ////只有当列表文件不存在时才打印列表
38 | GraphList.AppendRule();
39 |
40 | //Debug.WriteLine(@"CACHR> GraphList加入规则完成");
41 |
42 | //Debug.WriteLine(@"CACHR> Fiddler初始化开始");
43 | FiddlerRules.Initialize();
44 | //Debug.WriteLine(@"CACHR> Fiddler初始化完成");
45 |
46 | //Debug.WriteLine(@"CACHR> 初始化完成");
47 | }
48 |
49 | ~KanColleCacher()
50 | {
51 | Settings.Save();
52 | Debug.Flush();
53 | }
54 |
55 | public string ToolName
56 | {
57 | get { return name; }
58 | }
59 |
60 | public object GetToolView()
61 | {
62 | return view;
63 | }
64 |
65 | public object GetSettingsView()
66 | {
67 | return null;
68 | }
69 | }
70 |
71 | [Export(typeof(INotifier))]
72 | [ExportMetadata("Title", AssemblyInfo.Name)]
73 | [ExportMetadata("Description", AssemblyInfo.Description)]
74 | [ExportMetadata("Version", AssemblyInfo.Version)]
75 | [ExportMetadata("Author", AssemblyInfo.Author)]
76 | public class KanColleCacher_Initializer : INotifier
77 | {
78 | public void Initialize()
79 | {
80 | KanColleCacher.Initialize();
81 | }
82 |
83 | public void Dispose()
84 | {
85 | }
86 |
87 | public object GetSettingsView()
88 | {
89 | return null;
90 | }
91 |
92 | public void Show(NotifyType type, string header, string body, System.Action activated, System.Action failed = null)
93 | {
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/KanColleCacher/KanColleCacher.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1FF4CF0A-E57E-433C-B5B0-5363C2425780}
8 | Library
9 | Properties
10 | d_f_32.KanColleCacher
11 | KanColleCacher
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | Libraries\FiddlerCore4.dll
37 | False
38 |
39 |
40 | Libraries\KanColleViewer.exe
41 | False
42 |
43 |
44 | Libraries\KanColleWrapper.dll
45 |
46 |
47 | Libraries\Livet.dll
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | CacherToolView.xaml
75 |
76 |
77 |
78 |
79 |
80 | Code
81 |
82 |
83 |
84 |
85 |
86 | Designer
87 | MSBuild:Compile
88 |
89 |
90 |
91 |
98 |
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/FiddlerCore4.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleCacher/Libraries/FiddlerCore4.dll
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/KanColleViewer.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleCacher/Libraries/KanColleViewer.exe
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/KanColleWrapper.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleCacher/Libraries/KanColleWrapper.dll
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/Livet.Extensions.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleCacher/Libraries/Livet.Extensions.dll
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/Livet.Extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Livet.Extensions
5 |
6 |
7 |
8 |
9 | Show folder browser dialog.
10 | for .
11 | This object must be hosted by .
12 |
13 |
14 |
15 |
16 | Invokes the action related to this class.
17 |
18 | specified to in the client.
19 |
20 |
21 |
22 | implementation based on Win32 Common Item Dialog.
23 |
24 |
25 |
26 |
27 | Hides implementation of the folder selection dialog which depends on platform version.
28 |
29 |
30 |
31 |
32 | Initializes a new instance of the class.
33 |
34 |
35 |
36 |
37 | Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
38 |
39 |
40 |
41 |
42 | Releases unmanaged and - optionally - managed resources
43 |
44 |
45 | true to release both managed and unmanaged resources; false to release only unmanaged resources.
46 |
47 |
48 |
49 |
50 | Shows the dialog with specified as host window.
51 |
52 | The host window which will host this dialog.
53 | The result of the dialog.
54 |
55 | is null.
56 |
57 |
58 |
59 |
60 | Shows the dialog with specified as host window.
61 |
62 | The host window which will host this dialog. This value will not be null.
63 | The result of the dialog.
64 |
65 |
66 |
67 | Gets or sets the selected path.
68 |
69 |
70 | The selected path. This will be default path when the dialog is opened.
71 |
72 |
73 |
74 |
75 | Gets or sets the descriptive text to instruct the operation.
76 |
77 |
78 | The descriptive text to instruct the operation.
79 | Some dialog cannot support this value.
80 |
81 |
82 |
83 |
84 | Gets or sets the title of the dialog.
85 |
86 |
87 | The title of the dialog.
88 | Some dialog cannot support this value.
89 | The null or empty indicates using default title.
90 |
91 |
92 |
93 |
94 | Initializes a new instance of the class.
95 |
96 |
97 |
98 |
99 | Releases unmanaged and - optionally - managed resources
100 |
101 | true to release both managed and unmanaged resources; false to release only unmanaged resources.
102 |
103 |
104 |
105 | Shows the dialog with specified as host window.
106 |
107 | The host window which will host this dialog. This value will not be null.
108 |
109 | The result of the dialog.
110 |
111 |
112 |
113 |
114 | Gets or sets the title of the dialog.
115 |
116 |
117 | The title of the dialog.
118 | Some dialog cannot support this value.
119 | The null or empty indicates using default title.
120 |
121 |
122 |
123 |
124 | This property is not supported.
125 |
126 | Always .
127 |
128 |
129 |
130 | Gets or sets the selected path.
131 |
132 |
133 | The selected path. This will be default path when the dialog is opened.
134 |
135 |
136 |
137 |
138 | implementation using WinForm's .
139 |
140 |
141 |
142 |
143 | Initializes a new instance of the class.
144 |
145 |
146 |
147 |
148 | Releases unmanaged and - optionally - managed resources
149 |
150 | true to release both managed and unmanaged resources; false to release only unmanaged resources.
151 |
152 |
153 |
154 | Shows the dialog with specified as host window.
155 |
156 | The host window which will host this dialog. This value will not be null.
157 |
158 | The result of the dialog.
159 |
160 |
161 |
162 |
163 | Gets or sets the descriptive text to instruct the operation.
164 |
165 |
166 | The descriptive text to instruct the operation.
167 |
168 |
169 |
170 |
171 | Gets or sets the selected path.
172 |
173 |
174 | The selected path. This will be default path when the dialog is opened.
175 |
176 |
177 |
178 |
179 | This property is not supported.
180 |
181 | Always .
182 |
183 |
184 |
185 | A simple implementation wraps HWND.
186 |
187 |
188 |
189 |
190 | Initializes a new instance of the class.
191 |
192 | The HWND.
193 |
194 |
195 |
196 | Gets the handle to the window represented by the implementer.
197 |
198 |
199 | A handle to the window represented by the implementer.
200 |
201 |
202 |
203 |
204 | The factory to abstract concrete implementation of .
205 |
206 |
207 |
208 |
209 | Creates the appropriate for running platform.
210 |
211 | Folder selection dialog impementation preference.
212 |
213 | The appropriate for running platform.
214 |
215 |
216 | The return value is determined with running platform and .
217 |
218 |
219 |
220 |
221 | Gets a value indicating whether this platform can use Win32 Common Item Dialog.
222 |
223 |
224 | true if this platform can use Win32 Common Item Dialog; otherwise, false.
225 |
226 |
227 |
228 |
229 | Specify preferred selection dialog implementation.
230 |
231 |
232 | When you have to use specific dialog implementation
233 | regardless of underlying platform then specify this value explicitly.
234 |
235 |
236 |
237 |
238 | Do not specify any preference.
239 | Livet selects most appropriate options for current platform.
240 |
241 |
242 |
243 |
244 | Specifies using always .
245 |
246 |
247 |
248 |
249 | Specifies using Win32 Common Item Dialog (namely, IFileOpenDialig).
250 | When the platform is prior to Windows Vista/Windows Server 2008,
251 | is thrown.
252 |
253 |
254 |
255 |
256 | for folder selection with folder browser dialog.
257 |
258 |
259 |
260 | You should set both of and
261 | when you set each of them,
262 | because some platform only supports one of them.
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 | Initializes a new instance of the class
272 | wihtout message key.
273 |
274 |
275 |
276 |
277 | Initializes a new instance of the class
278 | with message key.
279 |
280 | The message key to identify the message instance.
281 |
282 |
283 |
284 | Creates the new instance of this class.
285 |
286 | The new which has identical properties to this instance.
287 |
288 |
289 |
290 | Defines depencendy property.
291 |
292 |
293 | .
294 |
295 |
296 |
297 |
298 | Defines depencendy property.
299 |
300 |
301 | .
302 |
303 |
304 |
305 |
306 | Defines depencendy property.
307 |
308 |
309 | .
310 |
311 |
312 |
313 |
314 | Defines depencendy property.
315 |
316 |
317 | .
318 |
319 |
320 |
321 |
322 | Gets or sets the description for folder selection dialog.
323 |
324 |
325 | The description for folder browser dialog.
326 |
327 |
328 |
329 |
330 | Gets or sets the selected path on the folder browser dialog.
331 |
332 |
333 | The title for folder selection dialog.
334 | If this value is null or empty, then the default title is used.
335 |
336 |
337 |
338 |
339 | Gets or sets the selected path on the folder selection dialog.
340 |
341 |
342 | The selected path on the folder selection dialog.
343 | This value is also used to initial selected folder in the dialog.
344 |
345 |
346 |
347 |
348 | Gets or sets the dialog preference.
349 |
350 |
351 | The dialog preference. Default is .
352 |
353 |
354 |
355 |
356 | ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。
357 |
358 |
359 |
360 |
361 | このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。
362 |
363 |
364 |
365 |
366 | 厳密に型指定されたこのリソース クラスを使用して、すべての検索リソースに対し、
367 | 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。
368 |
369 |
370 |
371 |
372 | Common Item Dialog is supported from Windows Vista/Windows Server 2008. に類似しているローカライズされた文字列を検索します。
373 |
374 |
375 |
376 |
377 | Unsupported '{0}' value '{1}'({2},0x{2:x8}). に類似しているローカライズされた文字列を検索します。
378 |
379 |
380 |
381 |
382 |
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/Livet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleCacher/Libraries/Livet.dll
--------------------------------------------------------------------------------
/KanColleCacher/Libraries/Portable.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleCacher/Libraries/Portable.dll
--------------------------------------------------------------------------------
/KanColleCacher/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Diagnostics;
7 | using System.IO;
8 |
9 |
10 |
11 |
12 | /*
13 | * 输出调试信息的规则
14 | * ------------------------------
15 | * 【头引用】
16 | * using Debug = System.Diagnostics.Debug;
17 | *
18 | * 【输出调试】
19 | * (只输出到调试监听)
20 | * Debug.WriteLine("CACHR> (信息内容)");
21 | *
22 | * 【输出警告】
23 | * 希望被人看到的警告信息(输出到调试监听 和 错误文件)
24 | * Log.Warning(sender, "警告信息", "警告信息2"...)
25 | *
26 | * 【输出异常】
27 | * 希望被人看到的错误信息(输出到调试监听 和 错误文件)
28 | * Log.Execption(sender, ex, message)
29 | */
30 |
31 |
32 | namespace d_f_32.KanColleCacher
33 | {
34 |
35 | class Log
36 | {
37 | const string path = "error2.log";
38 | const string wrMsg = @"
39 | ===================================KAN==COLLE==CACHER======
40 | WARNING date = {0}, sender = {1}, message =
41 | {2}
42 | ";
43 | const string exMsg = @"
44 | ===================================KAN==COLLE==CACHER======
45 | ERROR date = {0}, sender = {1},
46 | message = {2}
47 | exception = {3}
48 | ";
49 | const string wrFmt = @"
50 | CACHR> {0}
51 | ";
52 | const string exFmt = @"
53 | CACHR> {0}
54 | -------------------------------------------------
55 | {1}
56 | -------------------------------------------------
57 | ";
58 |
59 | //希望被人看到的警告信息
60 | static public void Warning(object sender, params string[] args)
61 | {
62 | var message = string.Join("\r\n\t\t ", args) + "\r\n";
63 | Debug.WriteLine(wrFmt, message);
64 |
65 | File.AppendAllText(path,
66 | string.Format(wrMsg,
67 | DateTimeOffset.Now,
68 | sender,
69 | message
70 | )
71 | );
72 |
73 | Debug.Flush();
74 | }
75 |
76 | //希望被人看到的错误信息
77 | static public void Exception(object sender, Exception exception, string describe)
78 | {
79 | Debug.WriteLine(exFmt, describe, exception);
80 |
81 | try
82 | {
83 | var message = string.Format(
84 | exMsg,
85 | DateTimeOffset.Now,
86 | sender,
87 | describe,
88 | exception
89 | );
90 |
91 | File.AppendAllText(path, message);
92 | }
93 | catch (Exception ex)
94 | {
95 | Debug.WriteLine("CACHR> Log.Exception()异常");
96 | Debug.WriteLine(" "+ex.Message);
97 | }
98 |
99 | Debug.Flush();
100 | }
101 |
102 | #if DEBUG
103 | static int print = 0;
104 | [Obsolete("仅用于Debug模式")]
105 | static public void PrintOnce(string head)
106 | {
107 | if (print >= 2)
108 | return;
109 |
110 | print++;
111 |
112 | File.AppendAllText("head.txt", head + "\r\n\r\n" );
113 | }
114 |
115 | #endif
116 |
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/KanColleCacher/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using d_f_32.KanColleCacher;
6 | // 有关程序集的常规信息通过以下
7 | // 特性集控制。更改这些特性值可修改
8 | // 与程序集关联的信息。
9 | [assembly: AssemblyTitle(AssemblyInfo.Title)]
10 | [assembly: AssemblyDescription(AssemblyInfo.Description)]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct(AssemblyInfo.Name)]
14 | [assembly: AssemblyCopyright(AssemblyInfo.Copyright)]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 |
18 | // 将 ComVisible 设置为 false 使此程序集中的类型
19 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
20 | // 则将该类型上的 ComVisible 特性设置为 true。
21 | [assembly: ComVisible(false)]
22 |
23 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
24 | //[assembly: Guid("7909a3f7-15a8-4ee5-afc5-11a7cfa40576")]
25 | [assembly: Guid("DA0FF655-A2CA-40DC-A78B-6DC85C2D448B")]
26 |
27 | // 程序集的版本信息由下面四个值组成:
28 | //
29 | // 主版本
30 | // 次版本
31 | // 生成号
32 | // 修订号
33 | //
34 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
35 | // 方法是按如下所示使用“*”:
36 | // [assembly: AssemblyVersion("1.0.*")]
37 | [assembly: AssemblyVersion(AssemblyInfo.Version)]
38 | [assembly: AssemblyFileVersion(AssemblyInfo.Version)]
39 |
40 |
41 | namespace d_f_32.KanColleCacher
42 | {
43 | public static class AssemblyInfo
44 | {
45 |
46 | public const string Name = "KanColleCacher";
47 | public const string Version = "2.1.0.25";
48 | public const string Author = "d.f.32";
49 | public const string Copyright = "©2014 - d.f.32";
50 | #if DEBUG
51 | public const string Title = "提督很忙!缓存工具 (DEBUG)";
52 | #else
53 | public const string Title = "提督很忙!缓存工具";
54 | #endif
55 | public const string Description = "通过创建本地缓存以加快游戏加载速度(并支持魔改)";
56 | }
57 | }
--------------------------------------------------------------------------------
/KanColleCacher/RecentRecord.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace d_f_32.KanColleCacher
8 | {
9 | static class TaskRecord
10 | {
11 | static Dictionary record = new Dictionary();
12 | //KEY: url, Value: filepath
13 | //只有在验证文件修改时间后,向客户端返回本地文件或者将文件保存到本地时才需要使用
14 |
15 | static public void Add(string url, string filepath)
16 | {
17 | if (record.ContainsKey(url))
18 | record[url] = filepath;
19 | else
20 | record.Add(url, filepath);
21 | }
22 |
23 | static public string GetAndRemove(string url)
24 | {
25 | string ret = Get(url);
26 | record.Remove(url);
27 | return ret;
28 | }
29 | static public string Get(string url)
30 | {
31 | if (record.ContainsKey(url))
32 | return record[url];
33 | return "";
34 | }
35 | }
36 |
37 | #region 废弃的代码
38 | //static class RecentRecord
39 | //{
40 | // class RecordItem
41 | // {
42 | // public string url = "";
43 | // public filetype type = filetype.not_file;
44 | // public string result = "";
45 | // public int state = 0;
46 | // //0 - 无记录;1 - 有效缓存;-1 - 需要更新的缓存
47 |
48 | // public RecordItem (string url, filetype type, string result, int state)
49 | // {
50 | // this.url = url;
51 | // this.type = type;
52 | // this.result = result;
53 | // this.state = state;
54 | // }
55 | // }
56 |
57 | // static List list = new List();
58 |
59 | // static public void Add(string url, filetype type, string result, int state)
60 | // {
61 | // if (list.Count > 20)
62 | // list.RemoveAt(0);
63 |
64 | // list.Add(new RecordItem(url, type, result, state));
65 | // }
66 |
67 | // static public int Get(string url, out filetype type, out string result)
68 | // {
69 | // var item = list.Find(x => x.url == url);
70 | // if (item != null)
71 | // {
72 | // type = item.type;
73 | // result = item.result;
74 | // return item.state;
75 | // }
76 |
77 | // type = filetype.not_file;
78 | // result = "";
79 | // return 0;
80 | // }
81 |
82 | //}
83 | #endregion
84 | }
85 |
--------------------------------------------------------------------------------
/KanColleCacher/Settings - 副本.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Debug = System.Diagnostics.Debug;
8 | using d_f_32.KanColleCacher.Configuration;
9 | using System.ComponentModel;
10 | using System.ComponentModel.Composition;
11 | using System.Runtime.CompilerServices;
12 |
13 |
14 | namespace d_f_32.KanColleCacher
15 | {
16 | [Serializable]
17 | public class Settings : INotifyPropertyChanged
18 | {
19 | private static string filePath;
20 |
21 | public static Settings Current { get; private set; }
22 |
23 | ///
24 | /// 加载插件设置
25 | ///
26 | public static void Load()
27 | {
28 | filePath = Directory.GetCurrentDirectory() + @"\Plugins\KanColleCacher.ini";
29 |
30 | if (!File.Exists(filePath))
31 | {
32 | var path = Path.Combine(
33 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
34 | "grabacr.net",
35 | "KanColleViewer",
36 | "KanColleCacher.ini"
37 | );
38 | if (File.Exists(path))
39 | filePath = path;
40 | }
41 |
42 | if (File.Exists(filePath))
43 | {
44 | var _Parser = ConfigParser.ReadIniFile(filePath);
45 | Current = _Parser.DeserializeObject("Settings");
46 |
47 | try
48 | {
49 | Directory.CreateDirectory(Current.CacheFolder);
50 | }
51 | catch (Exception ex)
52 | {
53 | Current.CacheFolder = Directory.GetCurrentDirectory() + @"\MyCache";
54 | Log.Exception(ex.InnerException, ex, "设置文件中CacheFolder不存在,试图创建时发生异常");
55 | }
56 | }
57 | else
58 | {
59 | //设置文件丢失
60 | }
61 |
62 | Current = Current ?? new Settings();
63 | }
64 |
65 | ///
66 | /// 保存设置
67 | ///
68 | public static void Save()
69 | {
70 | try
71 | {
72 | var _Parser = File.Exists(filePath)
73 | ? ConfigParser.ReadIniFile(filePath)
74 | : new ConfigParser();
75 |
76 | _Parser.SerializeObject(Current, "Settings");
77 | _Parser.SaveIniFile(filePath);
78 | }
79 | catch (Exception ex)
80 | {
81 | Log.Exception(ex.InnerException, ex, "保存设置文件时出现异常");
82 | }
83 | }
84 |
85 |
86 | public Settings ()
87 | {
88 | _CacheFolder = Directory.GetCurrentDirectory() + @"\MyCache";
89 | _CacheEnabled = true;
90 | _HackEnabled = true;
91 | _HackTitleEnabled = true;
92 |
93 | _CacheEntryFiles = 2;
94 | _CachePortFiles = 2;
95 | _CacheSceneFiles = 2;
96 | _CacheResourceFiles = 2;
97 | _CacheSoundFiles = 2;
98 |
99 | _CheckFiles = 1;
100 | _PrintGraphList = true;
101 | }
102 |
103 |
104 |
105 | private string _CacheFolder;
106 | [ExportMetadata("Comment","缓存文件夹")]
107 | public string CacheFolder
108 | {
109 | get { return this._CacheFolder; }
110 | set
111 | {
112 | if (this._CacheFolder != value)
113 | {
114 | this._CacheFolder = value;
115 | this.RaisePropertyChanged();
116 | }
117 | }
118 | }
119 |
120 | private bool _CacheEnabled;
121 | [ExportMetadata("Comment", "启用缓存功能")]
122 | public bool CacheEnabled
123 | {
124 | get { return this._CacheEnabled; }
125 | set
126 | {
127 | if (this._CacheEnabled != value)
128 | {
129 | this._CacheEnabled = value;
130 | this.RaisePropertyChanged();
131 | }
132 | }
133 | }
134 |
135 | private bool _HackEnabled;
136 | [ExportMetadata("Comment", "启用Hack规则")]
137 | public bool HackEnabled
138 | {
139 | get { return this._HackEnabled; }
140 | set
141 | {
142 | if (this._HackEnabled != value)
143 | {
144 | this._HackEnabled = value;
145 | this.RaisePropertyChanged();
146 | }
147 | }
148 | }
149 |
150 | private bool _HackTitleEnabled;
151 | [ExportMetadata("Comment", "启用针对TitleCall与WorldName的特殊规则")]
152 | public bool HackTitleEnabled
153 | {
154 | get { return this._HackTitleEnabled; }
155 | set
156 | {
157 | if (this._HackTitleEnabled != value)
158 | {
159 | this._HackTitleEnabled = value;
160 | this.RaisePropertyChanged();
161 | }
162 | }
163 | }
164 |
165 | private int _CacheEntryFiles;
166 | public int CacheEntryFiles
167 | {
168 | get { return this._CacheEntryFiles; }
169 | set
170 | {
171 | if (this._CacheEntryFiles != value)
172 | {
173 | this._CacheEntryFiles = value;
174 | this.RaisePropertyChanged();
175 | }
176 | }
177 | }
178 |
179 | private int _CachePortFiles;
180 | public int CachePortFiles
181 | {
182 | get { return this._CachePortFiles; }
183 | set
184 | {
185 | if (this._CachePortFiles != value)
186 | {
187 | this._CachePortFiles = value;
188 | this.RaisePropertyChanged();
189 | }
190 | }
191 | }
192 |
193 | private int _CacheSceneFiles;
194 | public int CacheSceneFiles
195 | {
196 | get { return this._CacheSceneFiles; }
197 | set
198 | {
199 | if (this._CacheSceneFiles != value)
200 | {
201 | this._CacheSceneFiles = value;
202 | this.RaisePropertyChanged();
203 | }
204 | }
205 | }
206 |
207 | private int _CacheResourceFiles;
208 | public int CacheResourceFiles
209 | {
210 | get { return this._CacheResourceFiles; }
211 | set
212 | {
213 | if (this._CacheResourceFiles != value)
214 | {
215 | this._CacheResourceFiles = value;
216 | this.RaisePropertyChanged();
217 | }
218 | }
219 | }
220 |
221 | private int _CacheSoundFiles;
222 | public int CacheSoundFiles
223 | {
224 | get { return this._CacheSoundFiles; }
225 | set
226 | {
227 | if (this._CacheSoundFiles != value)
228 | {
229 | this._CacheSoundFiles = value;
230 | this.RaisePropertyChanged();
231 | }
232 | }
233 | }
234 |
235 | private int _CheckFiles;
236 | [ExportMetadata("Comment", @"向服务器发送文件验证请求
237 | ; 0 - 不验证;1 - 不验证资源SWF文件;2 - 验证所有SWF文件
238 | ; 验证文件比不验证需要更长的加载时间,但可以避免游戏更新造成本地文件失效")]
239 | public int CheckFiles
240 | {
241 | get { return this._CheckFiles; }
242 | set
243 | {
244 | if (this._CheckFiles != value)
245 | {
246 | this._CheckFiles = value;
247 | this.RaisePropertyChanged();
248 | }
249 | }
250 | }
251 |
252 | private bool _PrintGraphList;
253 | ///
254 | /// 输出舰娘立绘的文件名列表
255 | /// 只有当缓存文件夹中的GraphList.txt不存在时才输出
256 | /// 只在游戏加载时有效
257 | ///
258 | [ExportMetadata("Comment", "启用缓存功能")]
259 | public bool PrintGraphList
260 | {
261 | get { return this._PrintGraphList; }
262 | set
263 | {
264 | if (this._PrintGraphList != value)
265 | {
266 | this._PrintGraphList = value;
267 | this.RaisePropertyChanged();
268 | }
269 | }
270 | }
271 |
272 | public event PropertyChangedEventHandler PropertyChanged;
273 |
274 | void RaisePropertyChanged([CallerMemberName] String propertyName = "")
275 | {
276 | if (PropertyChanged != null)
277 | {
278 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
279 | }
280 | }
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/KanColleCacher/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Debug = System.Diagnostics.Debug;
8 | using d_f_32.KanColleCacher.Configuration;
9 | using System.ComponentModel;
10 | using System.ComponentModel.Composition;
11 | using System.Runtime.CompilerServices;
12 |
13 |
14 | namespace d_f_32.KanColleCacher
15 | {
16 | [Serializable]
17 | public class Settings : INotifyPropertyChanged
18 | {
19 | private static string filePath;
20 |
21 | public static Settings Current { get; private set; }
22 |
23 | ///
24 | /// 加载插件设置
25 | ///
26 | public static void Load()
27 | {
28 | filePath = Directory.GetCurrentDirectory() + @"\Plugins\KanColleCacher.ini";
29 |
30 | if (!File.Exists(filePath))
31 | {
32 | var path = Path.Combine(
33 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
34 | "grabacr.net",
35 | "KanColleViewer",
36 | "KanColleCacher.ini"
37 | );
38 | if (File.Exists(path))
39 | filePath = path;
40 | }
41 |
42 | if (File.Exists(filePath))
43 | {
44 | var _Parser = ConfigParser.ReadIniFile(filePath);
45 | Current = _Parser.DeserializeObject("Settings");
46 |
47 | try
48 | {
49 | Directory.CreateDirectory(Current.CacheFolder);
50 | }
51 | catch (Exception ex)
52 | {
53 | Current.CacheFolder = Directory.GetCurrentDirectory() + @"\MyCache";
54 | Log.Exception(ex.InnerException, ex, "设置文件中CacheFolder不存在,试图创建时发生异常");
55 | }
56 | }
57 | else
58 | {
59 | //设置文件丢失
60 | try
61 | {
62 |
63 | Current = new Settings();
64 | if (!Directory.Exists(Current.CacheFolder))
65 | Directory.CreateDirectory(Current.CacheFolder);
66 | Save();
67 | }
68 | catch (Exception ex)
69 | {
70 | Log.Exception("Settings.Load()", ex, "Current.new !ERROR");
71 | Debug.WriteLine(ex);
72 | }
73 | }
74 |
75 | try
76 | {
77 |
78 | Debug.WriteLine("Settings.Current.CacheFolder = " + Current.CacheFolder);
79 |
80 | }
81 | catch (Exception ex)
82 | {
83 | Log.Exception("Settings.Load()", ex, "Current== null");
84 | Debug.WriteLine(ex);
85 | }
86 | Current = Current ?? new Settings();
87 | Debug.Flush();
88 | }
89 |
90 | ///
91 | /// 保存设置
92 | ///
93 | public static void Save()
94 | {
95 | try
96 | {
97 | var _Parser = File.Exists(filePath)
98 | ? ConfigParser.ReadIniFile(filePath)
99 | : new ConfigParser();
100 |
101 | _Parser.SerializeObject(Current, "Settings");
102 | _Parser.SaveIniFile(filePath);
103 | }
104 | catch (Exception ex)
105 | {
106 | Log.Exception(ex.InnerException, ex, "保存设置文件时出现异常");
107 | }
108 | }
109 |
110 |
111 | public Settings ()
112 | {
113 | _CacheFolder = Directory.GetCurrentDirectory() + @"\MyCache";
114 | _CacheEnabled = true;
115 | _HackEnabled = true;
116 | _HackTitleEnabled = true;
117 |
118 | _CacheEntryFiles = 2;
119 | _CachePortFiles = 2;
120 | _CacheSceneFiles = 2;
121 | _CacheResourceFiles = 2;
122 | _CacheSoundFiles = 2;
123 |
124 | _CheckFiles = 1;
125 | //_SaveApiStart2 = true;
126 | }
127 |
128 |
129 |
130 | private string _CacheFolder;
131 | [ExportMetadata("Comment","缓存文件夹")]
132 | public string CacheFolder
133 | {
134 | get { return this._CacheFolder; }
135 | set
136 | {
137 | if (this._CacheFolder != value)
138 | {
139 | this._CacheFolder = value;
140 | this.RaisePropertyChanged();
141 | }
142 | }
143 | }
144 |
145 | private bool _CacheEnabled;
146 | [ExportMetadata("Comment", "启用缓存功能")]
147 | public bool CacheEnabled
148 | {
149 | get { return this._CacheEnabled; }
150 | set
151 | {
152 | if (this._CacheEnabled != value)
153 | {
154 | this._CacheEnabled = value;
155 | this.RaisePropertyChanged();
156 | }
157 | }
158 | }
159 |
160 | private bool _HackEnabled;
161 | [ExportMetadata("Comment", "启用Hack规则")]
162 | public bool HackEnabled
163 | {
164 | get { return this._HackEnabled; }
165 | set
166 | {
167 | if (this._HackEnabled != value)
168 | {
169 | this._HackEnabled = value;
170 | this.RaisePropertyChanged();
171 | }
172 | }
173 | }
174 |
175 | private bool _HackTitleEnabled;
176 | [ExportMetadata("Comment", "启用针对TitleCall与WorldName的特殊规则")]
177 | public bool HackTitleEnabled
178 | {
179 | get { return this._HackTitleEnabled; }
180 | set
181 | {
182 | if (this._HackTitleEnabled != value)
183 | {
184 | this._HackTitleEnabled = value;
185 | this.RaisePropertyChanged();
186 | }
187 | }
188 | }
189 |
190 | private int _CacheEntryFiles;
191 | public int CacheEntryFiles
192 | {
193 | get { return this._CacheEntryFiles; }
194 | set
195 | {
196 | if (this._CacheEntryFiles != value)
197 | {
198 | this._CacheEntryFiles = value;
199 | this.RaisePropertyChanged();
200 | }
201 | }
202 | }
203 |
204 | private int _CachePortFiles;
205 | public int CachePortFiles
206 | {
207 | get { return this._CachePortFiles; }
208 | set
209 | {
210 | if (this._CachePortFiles != value)
211 | {
212 | this._CachePortFiles = value;
213 | this.RaisePropertyChanged();
214 | }
215 | }
216 | }
217 |
218 | private int _CacheSceneFiles;
219 | public int CacheSceneFiles
220 | {
221 | get { return this._CacheSceneFiles; }
222 | set
223 | {
224 | if (this._CacheSceneFiles != value)
225 | {
226 | this._CacheSceneFiles = value;
227 | this.RaisePropertyChanged();
228 | }
229 | }
230 | }
231 |
232 | private int _CacheResourceFiles;
233 | public int CacheResourceFiles
234 | {
235 | get { return this._CacheResourceFiles; }
236 | set
237 | {
238 | if (this._CacheResourceFiles != value)
239 | {
240 | this._CacheResourceFiles = value;
241 | this.RaisePropertyChanged();
242 | }
243 | }
244 | }
245 |
246 | private int _CacheSoundFiles;
247 | public int CacheSoundFiles
248 | {
249 | get { return this._CacheSoundFiles; }
250 | set
251 | {
252 | if (this._CacheSoundFiles != value)
253 | {
254 | this._CacheSoundFiles = value;
255 | this.RaisePropertyChanged();
256 | }
257 | }
258 | }
259 |
260 | private int _CheckFiles;
261 | [ExportMetadata("Comment", @"向服务器发送文件验证请求
262 | ; 0 - 不验证;1 - 不验证资源SWF文件;2 - 验证所有SWF文件
263 | ; 验证文件可以保证缓存的游戏文件始终是有效可用的,但因为要与服务器通信所以会比不验证花费更长的加载时间")]
264 | public int CheckFiles
265 | {
266 | get { return this._CheckFiles; }
267 | set
268 | {
269 | if (this._CheckFiles != value)
270 | {
271 | this._CheckFiles = value;
272 | this.RaisePropertyChanged();
273 | }
274 | }
275 | }
276 |
277 | // private bool _SaveApiStart2;
278 | // [ExportMetadata("Comment", @"保存 api_start2 通信数据以便生成 舰娘立绘的文件名列表。
279 | //; 只有当缓存文件夹中的 api_start2.dat 不存在时才会进行保存。
280 | //; 这一设置只有在游戏加载时才有效。")]
281 | // public bool SaveApiStart2
282 | // {
283 | // get { return this._SaveApiStart2; }
284 | // set
285 | // {
286 | // if (this._SaveApiStart2 != value)
287 | // {
288 | // this._SaveApiStart2 = value;
289 | // this.RaisePropertyChanged();
290 | // }
291 | // }
292 | // }
293 |
294 | #region 实现通知
295 |
296 | public event PropertyChangedEventHandler PropertyChanged;
297 |
298 | void RaisePropertyChanged([CallerMemberName] String propertyName = "")
299 | {
300 | if (PropertyChanged != null)
301 | {
302 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
303 | }
304 | }
305 |
306 | #endregion
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/KanColleCacher/ValidationRule.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows.Controls;
7 | using System.IO;
8 |
9 | namespace d_f_32.KanColleCacher
10 | {
11 | class FolderExistsRule:ValidationRule
12 | {
13 | public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
14 | {
15 | try
16 | {
17 | if (!Directory.Exists(value as string))
18 | return new ValidationResult(false, "无效的文件夹地址。");
19 |
20 | return new ValidationResult(true, null);
21 | }
22 | catch (Exception)
23 | {
24 | return new ValidationResult(false, "无效参数");
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/KanColleCacher/ValueConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows.Data;
7 | using System.Windows;
8 |
9 | namespace d_f_32.KanColleCacher
10 | {
11 | #region 保留的代码
12 | //[ValueConversion(typeof(int), typeof(bool?))]
13 | //class SwitchCaseConverter : IValueConverter
14 | //{
15 | // public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
16 | // {
17 | // if (parameter is string)
18 | // return (int)value == int.Parse((string)parameter);
19 |
20 | // return (int)value == (int)parameter;
21 | // }
22 |
23 | // public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
24 | // {
25 | // if ((bool?)value == true)
26 | // if (parameter is string)
27 | // return int.Parse((string)parameter);
28 | // else
29 | // return (int)parameter;
30 |
31 | // return DependencyProperty.UnsetValue;
32 | // }
33 | //}
34 |
35 | //[ValueConversion(typeof(int), typeof(bool?))]
36 | //class CacheFilesValueConverter: IValueConverter
37 | //{
38 | // public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
39 | // {
40 | // var _val = (int)value;
41 | // if (_val > 0) return true;
42 | // return false;
43 | // }
44 |
45 | // public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
46 | // {
47 | // var _val = (bool?) value;
48 | // if (_val == true) return 2;
49 | // return 0;
50 | // }
51 | //}
52 | #endregion
53 |
54 | [ValueConversion(typeof(int), typeof(bool?))]
55 | class ThreeStateValueConverter: IValueConverter
56 | {
57 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
58 | {
59 | var _val = (int)value;
60 | if (_val > 1) return true;
61 | else if (_val > 0) return null;
62 | return false;
63 | }
64 |
65 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
66 | {
67 | var _val = (bool?)value;
68 | if (_val == true) return 2;
69 | else if (_val == null) return 1;
70 | return 0;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/KanColleCacher/VersionChecker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Xml;
7 | using System.Xml.Linq;
8 | using System.IO;
9 | using Grabacr07.KanColleViewer.Models.Data.Xml;
10 | using Debug = System.Diagnostics.Debug;
11 |
12 | namespace d_f_32.KanColleCacher
13 | {
14 |
15 | class VersionChecker
16 | {
17 | static string filepath;
18 | static XDocument fileXML;
19 | static IEnumerable recordList;
20 |
21 | const string _RootName = "Last-Modified";
22 | const string _ItemElm = "Record";
23 | const string _ElmPath = "Path";
24 | const string _ElmTime = "Time";
25 | const string _ElmVersion = "Version";
26 |
27 | #region 加载与保存
28 |
29 | ///
30 | /// 从Last-Modified.xml加载记录
31 | ///
32 | static public void Load()
33 | {
34 | filepath = Settings.Current.CacheFolder + "\\Last-Modified.xml";
35 | try
36 | {
37 | if (File.Exists(filepath))
38 | fileXML = XDocument.Load(filepath);
39 | }
40 | catch (Exception ex)
41 | {
42 | Log.Exception(ex.InnerException, ex, "加载Last-Modified.xml时发生异常");
43 | }
44 |
45 | if (fileXML == null)
46 | {
47 | fileXML = new XDocument();
48 | fileXML.Add(new XElement(_RootName));
49 | }
50 |
51 | recordList = fileXML.Root.Elements();
52 | //recordList 和 fileXML是同步的
53 | }
54 |
55 | ///
56 | /// 保存到Last-Modified.xml
57 | ///
58 | static public void Save()
59 | {
60 | //try
61 | //{
62 | // var elms = fileXML.Descendants(_ItemElm)
63 | // .OrderBy(elm =>
64 | // { return elm.Element(_ElmPath).Value; }
65 | // ).ToArray();
66 |
67 | // fileXML.Root.Elements().Remove();
68 | // fileXML.Root.Add(elms);
69 | //}
70 | //catch(Exception ex)
71 | //{
72 | // Log.Warning(ex.InnerException,
73 | // "对xml文档排序时发生异常(元素损坏)",
74 | // "已停止排序",
75 | // ex.Message);
76 | //}
77 | try
78 | {
79 | fileXML.Save(filepath);
80 | }
81 | catch (Exception ex)
82 | {
83 | Log.Exception(ex.InnerException, ex, "保存Last-Modified.xml时发生异常");
84 | }
85 | }
86 | #endregion
87 |
88 |
89 |
90 | ///
91 | /// 添加记录
92 | ///
93 | /// 文件对应的uri
94 | /// 修改时间
95 | /// 文件版本
96 | static public void Add(Uri uri, string time)
97 | {
98 | string path = uri.AbsolutePath;
99 | if (!path.EndsWith(".swf"))
100 | return;
101 |
102 | string version = _GetVersionFromUri(uri);
103 |
104 | XElement elm;
105 | if (GetRecord(uri, out elm) > 0)
106 | {
107 | elm.SetElementValue(_ElmTime, time);
108 | elm.SetElementValue(_ElmVersion, version);
109 |
110 | //Debug.WriteLine("CACHR> 【UpdateRecord】" + path);
111 | }
112 | else
113 | {
114 | fileXML.Root.Add(new XElement(_ItemElm,
115 | new XElement(_ElmPath, uri.AbsolutePath),
116 | new XElement(_ElmTime, time),
117 | new XElement(_ElmVersion, version)
118 | )
119 | );
120 | //recordList = fileXML.Root.Elements();
121 | //Debug.WriteLine("CACHR> 【AddRecord】" + path);
122 |
123 | fileXML.Save(filepath);
124 | }
125 | }
126 |
127 |
128 | ///
129 | /// 获取文件记录
130 | ///
131 | /// 文件对应的uri
132 | /// 修改时间
133 | /// 文件版本
134 | ///
135 | static public int GetRecord(Uri uri, out XElement element)
136 | {
137 | element = null;
138 |
139 | foreach (XElement elm in recordList)
140 | {
141 | if (elm.Name == _ItemElm &&
142 | elm.Element(_ElmPath) != null &&
143 | elm.Element(_ElmPath).Value == uri.AbsolutePath)
144 | {
145 | element = elm;
146 |
147 | return 1;
148 | }
149 | }
150 |
151 | return 0;
152 | }
153 |
154 | ///
155 | /// 返回文件最后更改日期
156 | ///
157 | /// 文件对应的url
158 | /// 最后修改日期
159 | /// 1 - 返回日期;0 - 无日期记录;-1 - 文件类型不需要检测
160 | static public int GetFileLastTime(Uri uri, out string time)
161 | {
162 | time = "";
163 | string version = "";
164 | if (!uri.AbsolutePath.EndsWith(".swf"))
165 | //只有swf才需要检查修改时间
166 | return -1;
167 |
168 | XElement elm;
169 |
170 | if (GetRecord(uri, out elm) <= 0)
171 | return 0;
172 |
173 | time = elm.Element(_ElmTime) != null ?
174 | elm.Element(_ElmTime).Value :
175 | "";
176 | version = elm.Element(_ElmVersion) != null ?
177 | elm.Element(_ElmVersion).Value :
178 | "";
179 |
180 | string queryVer = _GetVersionFromUri(uri);
181 |
182 | if (!string.IsNullOrEmpty(queryVer) && version != queryVer)
183 | {
184 | //版本记录不符合 -> 返回无记录
185 | return 0;
186 | }
187 |
188 | return 1;
189 | }
190 |
191 |
192 | static string _GetVersionFromUri(Uri uri)
193 | {
194 | var query = uri.Query.ToLower();
195 | var pos = query.IndexOf("version=");
196 | if (pos < 0)
197 | return "";
198 |
199 | return query.Substring(pos + 8);
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/KanColleModifier.KCV.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1FF4CF0A-E57E-433C-B5B0-5363C2425780}
8 | Library
9 | Properties
10 | Gizeta.KanColleModifier.KCV
11 | KanColleModifier.KCV
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | Libraries\FiddlerCore4.dll
35 | False
36 |
37 |
38 | Libraries\KanColleViewer.exe
39 | False
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | ModifierView.xaml
58 |
59 |
60 |
61 |
62 |
63 |
64 | Designer
65 | MSBuild:Compile
66 |
67 |
68 |
69 |
76 |
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/Libraries/FiddlerCore4.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleModifier.KCV - 副本/Libraries/FiddlerCore4.dll
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/Libraries/KanColleViewer.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/df32/KanColleCacher/1298d9f7799fac94050e979fcad289163c41f2d5/KanColleModifier.KCV - 副本/Libraries/KanColleViewer.exe
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/Modifier.cs:
--------------------------------------------------------------------------------
1 | using Grabacr07.KanColleViewer.Composition;
2 | using System.ComponentModel.Composition;
3 |
4 | namespace Gizeta.KanColleModifier.KCV
5 | {
6 | [Export(typeof(IToolPlugin))]
7 | [ExportMetadata("Title", "KanColleModifier.KCV")]
8 | [ExportMetadata("Description", "KanColleViewer舰娘魔改插件。")]
9 | [ExportMetadata("Version", "1.1")]
10 | [ExportMetadata("Author", "@Gizeta")]
11 | public class Modifier : IToolPlugin
12 | {
13 | public string ToolName
14 | {
15 | get { return "魔改"; }
16 | }
17 |
18 | public object GetSettingsView()
19 | {
20 | return null;
21 | }
22 |
23 | public object GetToolView()
24 | {
25 | return new ModifierView();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/ModifierInitializer.cs:
--------------------------------------------------------------------------------
1 | using Grabacr07.KanColleViewer.Composition;
2 | using System.ComponentModel.Composition;
3 |
4 | namespace Gizeta.KanColleModifier.KCV
5 | {
6 | [Export(typeof(INotifier))]
7 | [ExportMetadata("Title", "ModifierInitializer")]
8 | [ExportMetadata("Description", "非Notifier,仅供KanColleModifier.KCV提前加载初始化使用。")]
9 | [ExportMetadata("Version", "1.0")]
10 | [ExportMetadata("Author", "@Gizeta")]
11 | public class ModifierInitializer : INotifier
12 | {
13 | public void Initialize()
14 | {
15 | ModifierView.Initialize();
16 | }
17 |
18 | public void Show(NotifyType type, string header, string body, System.Action activated, System.Action failed = null)
19 | {
20 | }
21 |
22 | public object GetSettingsView()
23 | {
24 | return null;
25 | }
26 |
27 | public void Dispose()
28 | {
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/ModifierView.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/ModifierView.xaml.cs:
--------------------------------------------------------------------------------
1 | using Fiddler;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 |
8 | namespace Gizeta.KanColleModifier.KCV
9 | {
10 | ///
11 | /// ModifierView.xaml 的交互逻辑
12 | ///
13 | public partial class ModifierView : UserControl
14 | {
15 | private static bool modifierOn = false;
16 | private static bool hasInitialize = false;
17 | private static Dictionary data = new Dictionary();
18 |
19 | public ModifierView()
20 | {
21 | InitializeComponent();
22 | readModifierData();
23 | showList();
24 | setModifier();
25 | toggle(modifierOn);
26 | }
27 |
28 | public static void Initialize()
29 | {
30 | if(File.Exists("modifier.enable"))
31 | {
32 | modifierOn = true;
33 | readModifierData();
34 | setModifier();
35 | }
36 | }
37 |
38 | private static void setModifier()
39 | {
40 | if (!hasInitialize)
41 | {
42 | hasInitialize = true;
43 | FiddlerApplication.BeforeRequest += FiddlerApplication_BeforeRequest;
44 | FiddlerApplication.BeforeResponse += FiddlerApplication_BeforeResponse;
45 | }
46 | }
47 |
48 | private static void FiddlerApplication_BeforeResponse(Session oSession)
49 | {
50 | if (modifierOn && oSession.fullUrl.IndexOf("/kcs/resources/swf/ships/") >= 0)
51 | {
52 | var tmp1 = oSession.fullUrl.Split('/');
53 | var tmp2 = tmp1.Last().Split('.');
54 | if (tmp2.Length >= 1)
55 | {
56 | if (data.ContainsKey(tmp2[0]))
57 | {
58 | oSession.utilDecodeResponse();
59 | oSession.ResponseBody = File.ReadAllBytes(data[tmp2[0]]);
60 | oSession.oResponse.headers.HTTPResponseCode = 200;
61 | oSession.oResponse.headers.HTTPResponseStatus = "200 OK";
62 | }
63 | }
64 | }
65 | }
66 |
67 | private static void FiddlerApplication_BeforeRequest(Session oSession)
68 | {
69 | if (modifierOn && oSession.fullUrl.IndexOf("/kcs/resources/swf/ships/") >= 0)
70 | {
71 | var tmp1 = oSession.fullUrl.Split('/');
72 | var tmp2 = tmp1.Last().Split('.');
73 | if (tmp2.Length >= 1)
74 | {
75 | if (data.ContainsKey(tmp2[0]))
76 | {
77 | oSession.bBufferResponse = true;
78 | }
79 | }
80 | }
81 | }
82 |
83 | private static void readModifierData()
84 | {
85 | if(File.Exists("魔改.txt"))
86 | {
87 | data.Clear();
88 | var file = File.Open("魔改.txt", FileMode.Open);
89 | using (var stream = new StreamReader(file))
90 | {
91 | while (!stream.EndOfStream)
92 | {
93 | var str = stream.ReadLine();
94 | var st = str.LastIndexOf('\\') + 1;
95 | var ed = str.LastIndexOf(".hack.swf");
96 | if (st > 0 && ed > 0)
97 | {
98 | if (File.Exists(str))
99 | {
100 | data.Add(str.Substring(st, ed - st), str);
101 | }
102 | }
103 | }
104 | }
105 | file.Close();
106 | }
107 | else
108 | {
109 | modifierOn = false;
110 | }
111 | }
112 |
113 | private void showList()
114 | {
115 | Modifier_List.Text = "魔改文件:";
116 | foreach (var d in data)
117 | {
118 | Modifier_List.Text += "\r\n" + d.Value;
119 | }
120 | }
121 |
122 | private void Modifier_Switch_Click(object sender, RoutedEventArgs e)
123 | {
124 | toggle(Modifier_Switch.Content.ToString() == "开启魔改");
125 | }
126 |
127 | private void Modifier_UpdateList_Click(object sender, RoutedEventArgs e)
128 | {
129 | readModifierData();
130 | showList();
131 | }
132 |
133 | private void toggle(bool on)
134 | {
135 | if(on)
136 | {
137 | modifierOn = true;
138 | Modifier_Switch.Content = "关闭魔改";
139 | if(!File.Exists("modifier.enable"))
140 | {
141 | File.Create("modifier.enable").Close();
142 | }
143 | }
144 | else
145 | {
146 | modifierOn = false;
147 | Modifier_Switch.Content = "开启魔改";
148 | if (File.Exists("modifier.enable"))
149 | {
150 | File.Delete("modifier.enable");
151 | }
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/KanColleModifier.KCV - 副本/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过以下
6 | // 特性集控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("KanColleModifier.KCV")]
9 | [assembly: AssemblyDescription("KanColleViewer舰娘魔改插件。")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("KanColleModifier.KCV")]
13 | [assembly: AssemblyCopyright("Copyright © 2014 Gizeta.")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("7909a3f7-15a8-4ee5-afc5-11a7cfa40576")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 生成号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.1.0.1")]
36 | [assembly: AssemblyFileVersion("1.1.0.1")]
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 d.f.32
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | KanColleCacher
2 | ====================
3 |
4 |
5 | 简介
6 | -----------------------
7 |
8 | KanColleCacher.dll为网页游戏「舰队Collection」的工具程序「提督很忙!」(KanColleViewer,即KCV)的扩展插件。
9 |
10 | KanColleCacher提供了在IE缓存外额外保存文件的功能(本地缓存)。
11 | 这一功能可以在一定程度上加快游戏加载速度,
12 | 并在此基础上支持魔改文件的使用。
13 |
14 |
15 |
16 |
17 | 功能介绍
18 | -----------------------
19 |
20 | ###本地缓存
21 |
22 | 本地缓存是KanColleCacher提供的主要功能。
23 |
24 | 当IE缓存被清除后,客户端将重新从服务器下载游戏所需的文件。
25 | KanColleCacher会在客户端向服务器请求文件之前,检查插件保存文件的“缓存文件夹”中是否存在相应的文件。
26 |
27 | * 若文件存在且有效,则直接向客户端返回缓存文件夹中的文件,因此不需要再次下载文件。
28 | * 若文件不存在,则客户端将下载文件,KanColleCacher将在文件下载完成后额外保存到缓存文件夹中。
29 |
30 |
31 | ###文件验证
32 |
33 | 游戏会不定期更新,更新后若客户端仍使用旧的缓存文件,则会使游戏发生异常。
34 | 为了避免出现这种情况,插件需要在返回缓存文件前对文件进行验证。
35 |
36 | 文件验证针对的是SWF文件,只有这类文件会导致游戏无法进行,并且游戏官方似乎并不会更新其他文件
37 |
38 | 文件验证功能可以通过设置开启或关闭。
39 |
40 |
41 |
42 |
43 | 版本与支持
44 | -----------------------
45 | * 目前KanColleCacher.dll为v2.1.0,
46 | * 支持KanColleViewer v3.3\v3.4\v3.6,
47 | * 理论上不支持3.0以前的KCV,
48 | * 不确定是否支持其他版本的KCV。
49 |
50 |
51 | 协议
52 | --------------------
53 | * 使用MIT开源协议发布。
54 |
55 |
56 |
57 |
58 | 使用方法
59 | --------------------
60 | * 将KanColleCacher.dll复制到KanColleViewer程序文件夹中的Plugins子文件夹中
61 | * 启动KanColleViewer
62 |
63 |
64 |
65 |
66 | 设置缓存文件夹
67 | --------------------
68 | * 启动KanColleViewer并进入游戏模后
69 | * 点击「工具」页面的「缓存工具」标签页以显示缓存工具的设置页面
70 | * 在「缓存文件夹」标签后的文本框中输入文件夹绝对地址。
71 | * 或者在启动KanColleViewer前
72 | * 在设置文件`KCV文件夹\Plugins\KanColleCacher.ini`中
73 | * 或在文件`%AppData%\grabacr.net\KanColleViewer\KanColleCacher.ini`中
74 | * 设置`CacheFolder`选项为缓存文件夹的绝对地址。
75 |
76 | > **注意:**
77 | >
78 | > 旧文件夹中的文件并不会被移动到新文件夹中;
79 | >
80 | > 新的文件夹地址将在下次KanColleViewer启动时生效。
81 |
82 |
83 |
84 | 使用外来的缓存文件
85 | --------------------
86 | 除了这个插件外还有其他的工具提供本地缓存的功能,例如岛风Go。
87 |
88 | 插件可以使用这些其他工具保存的缓存,但是(待续)
89 |
90 |
91 |
92 |
93 | 功能
94 | --------------------
95 |
96 | ###本地缓存
97 |
98 | 插件将在浏览器下载游戏文件后,将文件额外保存到缓存文件夹中。
99 | 文件的保存位置为:`[缓存文件夹] \ [文件URL的路径]`
100 | 例如:`E:\KanColleViewer\MyCache\kcs\scenes\TitleMain.swf`
101 | 缓存功能可以通过设置「启用缓存」(``)来启用或禁用。
102 |
103 |
104 |
105 | ###指定缓存文件夹
106 |
107 | 缓存文件夹的默认位置为KanColleViewer程序文件夹中的MyCache子文件夹。
108 | 当文件夹不存在时自动创建文件夹。
109 | 可以通过设置「缓存文件夹」(``)来指定缓存文件夹。
110 |
111 | > **注意:**
112 | >
113 | > 文件夹地址应当设置绝对地址;
114 | >
115 | > 旧文件夹中的文件并不会被移动到新文件夹中;
116 | >
117 | > 新的地址将在KanColleViewer下次启动时生效。
118 |
119 |
120 |
121 | ###文件分类与筛选
122 |
123 | KanColleViewer在设置中将游戏文件分为6类:
124 |
125 | * 入口文件:在游戏加载前,可能与用户和初始化有关的文件,包括Core.swf, mainD2.swf。
126 | * 载入文件:在出现GameStart按钮之前载入的文件,包括commonAsset.swf, font.swf, TitleMain.swf。
127 | * 界面文件:在按下GameStart按钮后到母港出现前载入的文件,包括PortMain.swf, sound_se.swf。
128 | * 场景文件
129 | * 资源文件
130 | * 声音文件
131 |
132 |
133 |
134 |
135 | ###Hack规则
136 | >
137 |
138 | ###服务器图标与标题音效的规则
139 | >
140 |
141 | ###文件版本校验
142 | >
143 |
144 |
145 |
146 |
147 |
148 | 原理与机制
149 | -------------------
150 |
151 | ###Fiddler
152 | Fiddler是KanColleViewer内嵌浏览器的代理器,KanColleViewer通过Fiddler收集游戏信息。
153 | KanColleCacher同样通过Fiddler来实现。
154 |
155 |
156 | ###会话
157 | 每一次KanColleViewer向游戏服务器请求数据或下载文件都是一次会话。
158 | 会话流程如下:
159 |
160 | 客户端
161 | ↓ 发送请求
162 | Fiddler
163 | ↓ 发送请求
164 | 服务器
165 | ↓ 返回数据
166 | Fiddler
167 | ↓ 返回数据
168 | 客户端
169 |
170 |
171 |
172 | ###Fiddler规则
173 | 缓存工具共在三处添加了规则:
174 |
175 |
176 | ####BeforeRequest
177 | 客户端向Fiddler发送请求后,Fiddler向服务器发送请求前执行的规则:
178 |
179 | 这是一个下载请求?
180 |
181 | ↓ →否,继续向服务器发送请求
182 |
183 | 插件缓存包含了请求的文件?
184 |
185 | ↓ →否,继续向服务器发送请求(新的文件的下载)
186 |
187 | 这个文件需要进行验证?
188 |
189 | ↓ →否,不再向服务器发送请求,直接由Fiddler返回缓存中的文件
190 |
191 | 将客户端的下载请求修改为文件验证请求,
192 | 验证缓存中的文件是否为最新文件。
193 | 若文件为最新的,则服务器将返回304代码;否则返回200代码与新文件。
194 |
195 |
196 | ####BeforeResponse
197 | 服务器向Fiddler返回数据后,Fiddler向客户端返回数据前:
198 |
199 | 服务器返回304代码?
200 |
201 | ↓ →否,继续向客户端返回数据
202 |
203 | 缓存文件为最新可用的文件。
204 | 将服务器返回的信息修改为200代码与缓存文件。
205 |
206 |
207 | ####AfterSessionComplete
208 | 整个会话结束后
209 |
210 | 服务器返回200代码?(完成了一个下载会话?)
211 |
212 | ↓ →否,忽略
213 |
214 | 这是服务器返回的200代码,而非插件修改的200代码?
215 |
216 | ↓ →否,忽略
217 |
218 | 刚刚下载了一个新的文件。
219 | 将新文件保存到缓存文件夹中,并返回的Last-Modified信息写入到缓存文件的最后修改时间中。
--------------------------------------------------------------------------------