/
Satorite.cs
283 lines (242 loc) · 8.95 KB
/
Satorite.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
using Satolist2.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Satolist2.Utility
{
//さとりて機能
static public class Satorite
{
//里々にロードさせるために、一時的に生成されるテキストファイル
private const string TemporaryDictionaryFileName = "dic_made_by_satolist.txt";
private const string SatoriProxyPath = "data/SatoriProxy.exe";
private const string SatoriteEventName = "Satolist2_Satorite_Event";
//里々のスクリプトをゴーストに送信
public static void SendSatori(GhostModel ghost, string script, EventType type)
{
if (ghost == null)
return;
var sakuraScript = ExecuteSatori(ghost, script, type);
if (!string.IsNullOrEmpty(sakuraScript))
{
SendSSTP(ghost, sakuraScript, true, false);
}
}
//里々のスクリプトをShioriEchoでゴーストに送信
public static void SendShioriEcho(GhostModel ghost, string script)
{
//ShioriEcho としてイベントを送信する
string[] lines = DictionaryUtility.SplitLines(script);
NotifySSTP(ghost, "ShioriEcho", "ShioriEcho送信に失敗", lines);
}
//里々のスクリプトを実行してさくらスクリプト出力を返す
public static string ExecuteSatori(GhostModel ghost, string script, EventType type)
{
try
{
var saveData = new SaveDataBuilder(ghost);
var dictionaryDirectory = ".";
if (saveData.Loaded && saveData.SaveData.ContainsKey(Constants.VariableDictionaryDirectory))
{
dictionaryDirectory = null;
//ロードするよう指定されているフォルダの1つを取り出してそこに配置するようする
var sp = saveData.SaveData[Constants.VariableDictionaryDirectory].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var dir in sp)
{
//有効なフォルダを選択していたらそれを使用する
if (Directory.Exists(ghost.FullDictionaryPath + "/" + dir))
{
dictionaryDirectory = dir;
break;
}
}
}
//このタイミングで起動できなかったらゴーストがそもそも辞書をロードしようとしてない?
if (string.IsNullOrEmpty(dictionaryDirectory))
throw new Exception("辞書フォルダが無効");
//さとりてに読ませる辞書を作成
string filePath = ghost.FullDictionaryPath + "/" + dictionaryDirectory + "/" + TemporaryDictionaryFileName;
var writer = new StreamWriter(filePath, false, ghost.BootConf.DicEncoding);
writer.WriteLine(Constants.GetEventHead(type) + SatoriteEventName);
writer.WriteLine(script);
writer.Close();
//里々にリクエストを投げてイベントを実行
var result = ExecuteSatoriDLL(ghost.FullDictionaryPath, SatoriteEventName);
//実行を終えたので一時的な辞書を終了
File.Delete(filePath);
//出力されたさくらスクリプトをSSTPでゴーストに投げる
var parsedResult = new ProtocolBuilder();
parsedResult.Deserialize(result);
if (parsedResult.Parameters.ContainsKey("Value"))
{
string resultScript = parsedResult.Parameters["Value"];
return resultScript;
}
}
catch { }
return null;
}
//SEND SSTPの送信
public static void SendSSTP(SakuraFMORecord fmoRecord, string script, bool useOwnedSSTP, bool noTranslate, IntPtr hWnd = default(IntPtr))
{
var sstpBuilder = new ProtocolBuilder();
sstpBuilder.Command = "SEND SSTP/1.0";
sstpBuilder.Parameters["Script"] = script;
sstpBuilder.Parameters["Charset"] = "Shift_JIS";
sstpBuilder.Parameters["Sender"] = "さとりすと";
if (hWnd != default(IntPtr))
{
sstpBuilder.Parameters["HWnd"] = hWnd.ToString();
}
if (noTranslate)
{
sstpBuilder.Parameters["Option"] = "notranslate";
}
if (useOwnedSSTP)
{
sstpBuilder.Parameters["ID"] = fmoRecord.ID;
}
RaiseSSTP(sstpBuilder, fmoRecord);
}
//SEND SSTPの送信
public static void SendSSTP(GhostModel ghost, string script, bool useOwnedSSTP, bool noTranslate, IntPtr hWnd = default(IntPtr))
{
var fmoRecord = SakuraFMOReader.Read(ghost);
if (fmoRecord == null)
{
throw new GhostNotFoundException();
}
SendSSTP(fmoRecord, script, useOwnedSSTP, noTranslate, hWnd);
}
//EXECUTE SSTPの送信
public static void ExecuteSSTP(GhostModel ghost, string command, IntPtr hWnd = default(IntPtr))
{
var sstpBuilder = new ProtocolBuilder();
sstpBuilder.Command = "EXECUTE SSTP/1.1";
sstpBuilder.Parameters["Sender"] = "さとりすと";
sstpBuilder.Parameters["Charset"] = "Shift_JIS";
sstpBuilder.Parameters["Command"] = command;
if (hWnd != default(IntPtr))
{
sstpBuilder.Parameters["HWnd"] = hWnd.ToString();
}
var fmoReader = new SakuraFMOReader();
fmoReader.Read();
var fmoRecord = fmoReader.Find(ghost);
if (fmoRecord == null)
throw new Exception(); //ゴーストが見つからない。おそらく、起動していない
RaiseSSTP(sstpBuilder, fmoRecord);
}
//NOTIFY SSTPの送信
public static void NotifySSTP(GhostModel ghost, string eventName, string fallbackScript = null, params string[] references)
{
var fmoRecord = SakuraFMOReader.Read(ghost);
if (fmoRecord == null)
{
throw new GhostNotFoundException();
}
var sstpBuilder = new ProtocolBuilder();
sstpBuilder.Command = "NOTIFY SSTP/1.0";
sstpBuilder.Parameters["Event"] = eventName;
sstpBuilder.Parameters["Sender"] = "さとりすと";
sstpBuilder.Parameters["Charset"] = "Shift_JIS";
for (int i = 0; i < references.Length; i++)
{
sstpBuilder.Parameters.Add(string.Format("Reference{0}", i), references[i]);
}
if (string.IsNullOrEmpty(fallbackScript))
{
sstpBuilder.Parameters["Script"] = fallbackScript;
}
RaiseSSTP(sstpBuilder, fmoRecord);
}
//NOTIFY SSTPブロードキャスト
public static void NotifySSTPBroadcast(string eventName, params string[] references)
{
var sstpBuilder = new ProtocolBuilder();
sstpBuilder.Command = "NOTIFY SSTP/1.0";
sstpBuilder.Parameters["Event"] = eventName;
sstpBuilder.Parameters["Sender"] = "さとりすと";
sstpBuilder.Parameters["Charset"] = "Shift_JIS";
for(int i = 0; i < references.Length; i++)
{
sstpBuilder.Parameters.Add(string.Format("Reference{0}", i), references[i]);
}
RaiseSSTPBroadcast(sstpBuilder);
}
//里々を起動
//さとりすと側のプロセスに依存せず別プロセスで里々を起動するのでさとりすとが64bitでも起動できるしくみ
private static string ExecuteSatoriDLL(string satoriDirectory, string eventName)
{
try
{
var startInfo = new ProcessStartInfo();
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = startInfo.WorkingDirectory + "/" + SatoriProxyPath;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.Arguments = string.Format("SendSatori \"{0}\" \"{1}\"", eventName, satoriDirectory);
startInfo.CreateNoWindow = true;
using(var process = Process.Start(startInfo))
{
var standardOutput = process.StandardOutput.ReadToEnd();
process.WaitForExit();
if (process.ExitCode != 0)
return string.Empty;
return standardOutput;
}
}
catch
{
return string.Empty;
}
}
//SSPへの単純データ送信
public static void RaiseSSTP(ProtocolBuilder data, SakuraFMORecord target)
{
var serializedData = data.Serialize();
var dataBytes = Constants.EncodingShiftJis.GetBytes(serializedData);
var dataPtr = Marshal.AllocHGlobal(dataBytes.Length);
Marshal.Copy(dataBytes, 0, dataPtr, dataBytes.Length);
var copydata = new Core.Win32Import.CopyDataStruct()
{
dwData = Core.Win32Import.SSTP_DWDATA,
cbData = (uint)dataBytes.Length,
lpData = dataPtr
};
var h = GCHandle.Alloc(copydata, GCHandleType.Pinned);
//TODO: マジックナンバーフラグの定数化
Core.Win32Import.SendMessageTimeoutA(target.HWnd, Core.Win32Import.WM_COPYDATA, IntPtr.Zero, h.AddrOfPinnedObject(), 2, 5000, IntPtr.Zero);
Marshal.FreeHGlobal(dataPtr);
}
//SSTPブロードキャスト
public static void RaiseSSTPBroadcast(ProtocolBuilder data)
{
var fmoReader = new SakuraFMOReader();
fmoReader.Read();
fmoReader.RemoveSurfacePreviewGeneratorRuntime();
foreach(var item in fmoReader.Records)
{
RaiseSSTP(data, item.Value);
}
}
//里々のバージョン取得
public static string GetSatoriVersion(GhostModel ghost)
{
return ExecuteSatori(ghost, "(里々のバージョン)", EventType.Word);
}
}
//ゴーストが見つかりません例外
public class GhostNotFoundException : Exception
{
public void PrintErrorLog()
{
Core.LogMessage.AddLog("編集中のゴーストにアクセスできません。SSPでゴーストを起動していますか?", Core.LogMessageType.Error);
}
}
}