OSDN Git Service

微修正。
[tpi/lychee.git] / src / plugin / cuiWrapper / cuiWrapper.cpp
1 /*******************************************************************************\r
2   TPI - flexible but useless plug-in framework.\r
3   Copyright (C) 2002-2009 Silky\r
4 \r
5   This library is free software; you can redistribute it and/or modify it under\r
6   the terms of the GNU Lesser General Public License as published by the Free\r
7   Software Foundation; either version 2.1 of the License, or (at your option)\r
8   any later version.\r
9 \r
10   This library is distributed in the hope that it will be useful, but WITHOUT\r
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \r
12   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License\r
13   for more details.\r
14 \r
15   You should have received a copy of the GNU Lesser General Public License along\r
16   with this library; if not, write to the Free Software Foundation, Inc.,\r
17   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
18 \r
19   $Id$\r
20 *******************************************************************************/\r
21 \r
22 //******************************************************************************\r
23 //    Includes\r
24 //******************************************************************************\r
25 \r
26 #define MYUSE_LIBPATH 1\r
27 #include "../../common/header/plugin.h"\r
28 #include "../../common/header/plugin-extra.h"\r
29 #include "../../common/library/library.h"\r
30 #include "../../common/library/xmldoc.h"\r
31 #include <wx/tokenzr.h>\r
32 #include "cuiWrapper.h"\r
33 #define min(a, b) ((a) < (b) ? (a) : (b))\r
34 \r
35 //******************************************************************************\r
36 //    Global varients\r
37 //******************************************************************************\r
38 \r
39 struct g_LibInfo\r
40 {\r
41         wxString szExeFile;\r
42         wxString szExeFileAlt;\r
43         wxString szListCommand;\r
44         wxULongLong_t nLibIndex;\r
45         wxXmlNode node;\r
46 }       g_LibInfo;\r
47 \r
48 TPI_PROC g_prProc;\r
49 \r
50 //******************************************************************************\r
51 //    Inside Functions\r
52 //******************************************************************************\r
53 \r
54 int myExecute(const wxString & szCommandLine, wxString * szOutput, const wxString & szCwd, bool fWine)\r
55 {\r
56 #ifdef __LINUX__\r
57         wxString sz = ::wxGetCwd();\r
58         ::wxSetWorkingDirectory(szCwd);\r
59         FILE * fp = popen(fWine ? (wxT("wine ") + szCommandLine).ToUTF8() : szCommandLine.ToUTF8(), "r");\r
60         ::wxSetWorkingDirectory(sz);\r
61         if (fp == NULL)\r
62         {\r
63                 wxLogError(L"Error :\n\nCommandLine:\n%s", szCommandLine.c_str());\r
64                 return TPI_ERROR_U_USE_LIBRARY;\r
65         }\r
66 \r
67         if (szOutput != NULL)\r
68         {\r
69                 char sz[32769];\r
70                 while (! feof(fp))\r
71                 {\r
72                         memset(sz, 0, sizeof(sz));\r
73                         fread(sz, sizeof(char), sizeof(sz) - 1, fp);\r
74                         * szOutput += UTF82String(sz);\r
75                 }\r
76 //              ::wxMessageBox(* szOutput);\r
77         }\r
78 \r
79         // 127を返した場合はコマンドが存在しない。\r
80         int nErrorCode = pclose(fp);\r
81         return (WIFEXITED(nErrorCode) && WEXITSTATUS(nErrorCode) != 127) ? TPI_ERROR_SUCCESS : TPI_ERROR_U_USE_LIBRARY;\r
82 #else\r
83         SECURITY_ATTRIBUTES sa;\r
84         memset(& sa, 0, sizeof(SECURITY_ATTRIBUTES));\r
85         sa.bInheritHandle = TRUE;\r
86         sa.nLength = sizeof(SECURITY_ATTRIBUTES);\r
87         HANDLE hRead, hWrite;\r
88         if (! ::CreatePipe(& hRead, & hWrite, & sa, 4096))\r
89         {\r
90                 return TPI_ERROR_U_USE_LIBRARY;\r
91         }\r
92         STARTUPINFO si;\r
93         memset(& si, 0, sizeof(STARTUPINFO));\r
94         si.cb = sizeof(STARTUPINFO);\r
95         si.dwFlags = STARTF_USESTDHANDLES;\r
96         si.hStdOutput = hWrite;\r
97         si.hStdError = hWrite;\r
98         PROCESS_INFORMATION pi;\r
99         if (! ::CreateProcess(NULL, szCommandLine.wchar_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, szCwd.IsEmpty() ? NULL : szCwd.wchar_str(), & si, & pi))\r
100         {\r
101                 return TPI_ERROR_U_USE_LIBRARY;\r
102         }\r
103         ::CloseHandle(pi.hThread);\r
104 \r
105         if (szOutput != NULL)\r
106         {\r
107                 bool bSignal;\r
108                 do\r
109                 {\r
110                         DWORD dwSize = 0;\r
111                         bSignal = ::WaitForSingleObject(pi.hProcess, 100) == WAIT_OBJECT_0;\r
112                         ::PeekNamedPipe(hRead, NULL, 0, NULL, & dwSize, NULL);\r
113                         while (dwSize > 0)\r
114                         {\r
115                                 char sz[4097];\r
116                                 memset(sz, 0, sizeof(sz));\r
117                                 ::ReadFile(hRead, & sz, sizeof(sz) - 1, & dwSize, NULL);\r
118                                 * szOutput += wxString(sz);\r
119                                 // UTF-8以外の文字コードだと、UTF82Stringを使うと書庫が開けなくなる。データの切り出しにも影響。\r
120 //                              * szOutput += UTF82String(sz);\r
121 //                              ::MessageBoxA(NULL, sz, NULL, 0);\r
122                                 ::PeekNamedPipe(hRead, NULL, 0, NULL, & dwSize, NULL);\r
123                         }\r
124                 }\r
125                 while (! bSignal);\r
126         }\r
127 \r
128         ::CloseHandle(pi.hProcess);\r
129         ::CloseHandle(hRead);\r
130         ::CloseHandle(hWrite);\r
131         return TPI_ERROR_SUCCESS;\r
132 #endif\r
133 }\r
134 \r
135 PosInfo MakePosInfo(const wxString & szPrefix)\r
136 {\r
137         PosInfo pi;\r
138         pi.nStart = myGetAttributeInt(& g_LibInfo.node, wxT("list-") + szPrefix + wxT("-s"));\r
139         pi.nCount = myGetAttributeInt(& g_LibInfo.node, wxT("list-") + szPrefix + wxT("-c"));\r
140         pi.nLine  = myGetAttributeInt(& g_LibInfo.node, wxT("list-") + szPrefix + wxT("-l"));\r
141         return pi;\r
142 }\r
143 \r
144 wxULongLong_t GetSize(const PosInfo & pi, wxULongLong_t nCurrent, const wxArrayString & as)\r
145 {\r
146         if (pi.nStart == 0 && pi.nCount == 0)\r
147         {\r
148                 return 0;\r
149         }\r
150 \r
151         wxULongLong_t nTemp = 0, nPos = nCurrent + pi.nLine;\r
152         wxString sz = pi.nCount == 0 ? as[nPos].Mid(pi.nStart) : as[nPos].Mid(pi.nStart, pi.nCount);\r
153         sz.ToULongLong(& nTemp);\r
154         return nTemp;\r
155 }\r
156 \r
157 //******************************************************************************\r
158 //    Functions\r
159 //******************************************************************************\r
160 \r
161 #ifdef __cplusplus\r
162 extern "C"\r
163 {\r
164 #endif\r
165 \r
166 int __stdcall GetPluginInformation\r
167 (\r
168         unsigned int _uInfoId,\r
169         wxULongLong_t,\r
170         void * _pPtr\r
171 )\r
172 {\r
173         if (_pPtr == NULL)\r
174         {\r
175                 return TPI_ERROR_D_PARAMETER;\r
176         }\r
177         switch (_uInfoId)\r
178         {\r
179         case TPI_INFO_VERSION_MAJOR:\r
180         case TPI_INFO_VERSION_MINOR:\r
181                 * (int *) _pPtr = 0;\r
182                 break;\r
183         case TPI_INFO_VERSION_API:\r
184                 * (int *) _pPtr = 2;\r
185                 break;\r
186         case TPI_INFO_HANDLE_ON_COMMAND:\r
187                 * (int *) _pPtr = 0;\r
188                 break;\r
189         default:\r
190                 return TPI_ERROR_D_UNSUPPORTED;\r
191         }\r
192         return TPI_ERROR_SUCCESS;\r
193 }\r
194 \r
195 int __stdcall GetFormatInformation(TPI_FORMATINFO * _fiInfo, bool _bFirst)\r
196 {\r
197         static wxULongLong_t s_nFileId;\r
198         static wxXmlDocument xmlDoc(myMakeXMLName(wxT("cuiWrapper")));\r
199         static wxXmlNode * xmlLibrary;\r
200         if (_bFirst)\r
201         {\r
202                 // xml解析開始。\r
203                 s_nFileId = 0;\r
204                 xmlLibrary = myGetFirstLib(& xmlDoc);\r
205         }\r
206         else\r
207         {\r
208                 xmlLibrary = myGetNextLib(xmlLibrary);\r
209         }\r
210         if (xmlLibrary == NULL)\r
211         {\r
212                 // データの終端に達した場合。\r
213                 return TPI_ERROR_S_ENDOFDATA;\r
214         }\r
215 \r
216         MakeFormatInfo(xmlLibrary, wxT("cuiWrapper"), _fiInfo, s_nFileId++);\r
217         if (myExecute(_fiInfo->szEngineName, NULL, wxEmptyString, _fiInfo->szEngineName.MakeLower().EndsWith(wxT(".exe"))) != TPI_ERROR_SUCCESS)\r
218         {\r
219                 _fiInfo->eSupportedCommand = 0;\r
220         }\r
221         return TPI_ERROR_SUCCESS;\r
222 }\r
223 \r
224 int __stdcall LoadPlugin\r
225 (\r
226         const wxString & _szArcName,\r
227         TPI_PROC _prProc,\r
228         wxULongLong_t _nTypeId\r
229 )\r
230 {\r
231         // xml解析開始。\r
232         wxXmlDocument xmlDoc(myMakeXMLName(wxT("cuiWrapper")));\r
233         wxXmlNode * xmlLibrary;\r
234 \r
235         // コールバック関数を設定。\r
236         if (_prProc != NULL)\r
237         {\r
238                 g_prProc = * _prProc;\r
239         }\r
240 \r
241         // 対象が存在するならば対応するライブラリを調査、\r
242         // 対象が存在しないならば指示されたライブラリをロード。\r
243         // 対象が空文字列なら処理を終了。\r
244         if (_szArcName.IsEmpty())\r
245         {\r
246                 return TPI_ERROR_SUCCESS;\r
247         }\r
248         if (! ::wxFileExists(_szArcName))\r
249         {\r
250                 xmlLibrary = myGetFirstLib(& xmlDoc, _nTypeId);\r
251                 if (xmlLibrary == NULL)\r
252                 {\r
253                         // xml文法エラー。\r
254                         return TPI_ERROR_UNDEFINED;\r
255                 }\r
256                 g_LibInfo.szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
257                 g_LibInfo.szExeFileAlt = xmlLibrary->GetAttribute(wxT("name-alt"), wxEmptyString);\r
258                 g_LibInfo.node = * xmlLibrary;\r
259                 g_LibInfo.nLibIndex = _nTypeId;\r
260                 return TPI_ERROR_SUCCESS;\r
261         }\r
262 \r
263         // 無限ループに陥らないよう上限を設定。\r
264         xmlLibrary = myGetFirstLib(& xmlDoc);\r
265         for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < 300 && xmlLibrary != NULL; g_LibInfo.nLibIndex++)\r
266         {\r
267                 // 書庫に対応しているかチェック。\r
268                 wxFileName fnArchive(_szArcName);\r
269                 wxArrayString asExt = ::wxStringTokenize(xmlLibrary->GetAttribute(wxT("suffix"), wxEmptyString), wxT(";"));\r
270                 if (xmlLibrary->HasAttribute(wxT("list")))\r
271                 {\r
272                         for (size_t i = 0; i < asExt.GetCount(); i++)\r
273                         {\r
274                                 // .tar.XXXなど二重判定への対応。\r
275 //                              if (asExt[i].IsSameAs(fnArchive.GetExt(), false))\r
276                                 if (fnArchive.GetFullName().MakeLower().EndsWith(wxT('.') + asExt[i].MakeLower()))\r
277                                 {\r
278                                         // ライブラリをロード。\r
279                                         g_LibInfo.szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
280                                         g_LibInfo.szExeFileAlt = xmlLibrary->GetAttribute(wxT("name-alt"), wxEmptyString);\r
281                                         g_LibInfo.node = * xmlLibrary;\r
282                                         g_LibInfo.nLibIndex = _nTypeId;\r
283                                         return TPI_ERROR_SUCCESS;\r
284                                 }\r
285                         }\r
286                 }\r
287                 xmlLibrary = myGetNextLib(xmlLibrary);\r
288         }\r
289         return TPI_ERROR_U_LOAD_LIBRARY;\r
290 }\r
291 \r
292 int __stdcall FreePlugin\r
293 (\r
294         void * // _pReserved\r
295 )\r
296 {\r
297         return TPI_ERROR_SUCCESS;\r
298 }\r
299 \r
300 int __stdcall OpenArchive\r
301 (\r
302         const wxString & _szArcName,\r
303         void * * _hArchive,\r
304         wxULongLong_t * _nFileCount\r
305 )\r
306 {\r
307         wxString szOutput;\r
308         TPI_SWITCHES swInfo;\r
309         swInfo.szArcName = _szArcName;\r
310         if (myExecute(g_LibInfo.szExeFile + wxT(" ") + MakeCommandLineSend(g_LibInfo.node.GetAttribute(wxT("list"), wxEmptyString), & swInfo), & szOutput, wxEmptyString, g_LibInfo.szExeFile.MakeLower().EndsWith(wxT(".exe"))) != TPI_ERROR_SUCCESS)\r
311         {\r
312                 return TPI_ERROR_U_USE_LIBRARY;\r
313         }\r
314         wxArrayString * as = new wxArrayString(::wxStringTokenize(szOutput, wxT("\r\n")));\r
315         * _hArchive = (void *) as;\r
316         as->Shrink();\r
317         if (_nFileCount != NULL)\r
318         {\r
319                 * _nFileCount = as->GetCount();\r
320         }\r
321 \r
322         wxString szStartLine = g_LibInfo.node.GetAttribute(wxT("list-line-s"), wxEmptyString);\r
323         if ((! szStartLine.IsEmpty() && as->Index(szStartLine) == wxNOT_FOUND) || as->IsEmpty())\r
324         {\r
325                 // 書庫が読み込めなかった?\r
326                 delete as;\r
327                 return TPI_ERROR_ARC_UNSUPPORTED;\r
328         }\r
329 \r
330         return TPI_ERROR_SUCCESS;\r
331 }\r
332 \r
333 int __stdcall CloseArchive\r
334 (\r
335         void * _hArchive\r
336 )\r
337 {\r
338         wxArrayString * as = (wxArrayString *) _hArchive;\r
339         as->Clear();\r
340         delete as;\r
341         return TPI_ERROR_SUCCESS;\r
342 }\r
343 \r
344 int __stdcall GetFileInformation\r
345 (\r
346         void * _hArchive,\r
347         TPI_FILEINFO * _fiInfo,\r
348         bool _bFirst\r
349 )\r
350 {\r
351         static size_t s_nCurrentLine;\r
352         static wxULongLong_t s_nFileId;\r
353         if (_hArchive == NULL)\r
354         {\r
355                 return TPI_ERROR_UNDEFINED;\r
356         }\r
357         wxArrayString asOutput = * (wxArrayString *) _hArchive;\r
358 \r
359         // XMLからの読み込みは初回に行う。\r
360         static wxString szEndLine, szDateFormat;\r
361         static wxULongLong_t nProcessPerLine;\r
362         static PosInfo piFName, piPSize, piUSize, piDate;\r
363         if (_bFirst)\r
364         {\r
365                 s_nFileId = 0;\r
366                 wxString szStartLine = g_LibInfo.node.GetAttribute(wxT("list-line-s"), wxEmptyString);\r
367                 if (! szStartLine.IsEmpty())\r
368                 {\r
369                         // 開始行の次の行にセット。エラーはOpenArchiveでチェック済み。\r
370                         s_nCurrentLine = asOutput.Index(szStartLine) + 1;\r
371                 }\r
372 \r
373                 // 初期設定。\r
374                 nProcessPerLine = myGetAttributeInt(& g_LibInfo.node, wxT("list-line-c"), 1);\r
375                 szEndLine = g_LibInfo.node.GetAttribute(wxT("list-line-e"), szStartLine);\r
376                 szDateFormat = g_LibInfo.node.GetAttribute(wxT("list-date-f"), wxDefaultDateTimeFormat);\r
377                 piFName = MakePosInfo(wxT("fname"));\r
378                 piPSize = MakePosInfo(wxT("psize"));\r
379                 piUSize = MakePosInfo(wxT("usize"));\r
380                 piDate  = MakePosInfo(wxT("date"));\r
381         }\r
382 \r
383         if (s_nCurrentLine >= asOutput.GetCount())\r
384         {\r
385                 // 空行で終わるとき以外はエラーとする。\r
386                 return szEndLine.IsEmpty() ? TPI_ERROR_S_ENDOFDATA : TPI_ERROR_ARC_UNSUPPORTED;\r
387         }\r
388 \r
389         // 最終行かどうか確認。\r
390         if (asOutput[s_nCurrentLine] == szEndLine)\r
391         {\r
392                 return TPI_ERROR_S_ENDOFDATA;\r
393         }\r
394 \r
395         // ファイル名を取得。\r
396         _fiInfo->szStoredName = piFName.nCount == 0 ? asOutput[s_nCurrentLine + piFName.nLine].Mid(piFName.nStart) : asOutput[s_nCurrentLine + piFName.nLine].Mid(piFName.nStart, piFName.nCount);\r
397         _fiInfo->szStoredName.Trim();\r
398         _fiInfo->fnFileName = wxFileName(_fiInfo->szStoredName, wxPATH_DOS);\r
399 \r
400         // サイズ取得。\r
401         _fiInfo->nPackedSize   = GetSize(piPSize, s_nCurrentLine, asOutput);\r
402         _fiInfo->nUnpackedSize = GetSize(piUSize, s_nCurrentLine, asOutput);\r
403 \r
404         // 更新時刻取得。\r
405         if (piDate.nStart != 0 || piDate.nCount != 0)\r
406         {\r
407                 _fiInfo->tmModify.ParseFormat(piDate.nCount == 0 ? asOutput[s_nCurrentLine + piDate.nLine].Mid(piDate.nStart) : asOutput[s_nCurrentLine + piDate.nLine].Mid(piDate.nStart, piDate.nCount), szDateFormat);\r
408         }\r
409 \r
410         // 最後に次の行へ進めておく。\r
411         _fiInfo->nFileId = s_nFileId++;\r
412         s_nCurrentLine += nProcessPerLine;\r
413 \r
414         return TPI_ERROR_SUCCESS;\r
415 }\r
416 \r
417 int __stdcall GetArchiveInformation\r
418 (\r
419         void *,\r
420         TPI_ARCHIVEINFO * _aiInfo\r
421 )\r
422 {\r
423         // 形式に関する情報を取得。\r
424         MakeFormatInfo(& g_LibInfo.node, wxT("cuiWrapper"), & _aiInfo->fiInfo, 0);\r
425         return TPI_ERROR_SUCCESS;\r
426 }\r
427 \r
428 int __stdcall Command\r
429 (\r
430         wxULongLong_t _eCommand,\r
431         TPI_SWITCHES * _swInfo,\r
432         void *,// _hArchive,\r
433         const wxArrayString & _szFiles\r
434 )\r
435 {\r
436         // xmlからコマンドラインを取得。\r
437         wxString szPath, szCommandLine;\r
438 \r
439         // APIアドレス取得。\r
440         if (! g_LibInfo.node.GetAttribute(\r
441                         _eCommand == TPI_COMMAND_CREATE  ? wxT("create") :\r
442                         _eCommand == TPI_COMMAND_ADD     ? wxT("add") :\r
443                         _eCommand == TPI_COMMAND_EXTRACT ? wxT("extract") : \r
444                         _eCommand == TPI_COMMAND_DELETE  ? wxT("delete") : \r
445                         _eCommand == TPI_COMMAND_UPDATE  ? wxT("update") : \r
446                         _eCommand == TPI_COMMAND_TEST    ? wxT("test") : \r
447                         _eCommand == TPI_COMMAND_REPAIR  ? wxT("repair") : \r
448                         _eCommand == TPI_COMMAND_MOVE    ? wxT("move") : \r
449                         _eCommand == TPI_COMMAND_SFX     ? wxT("sfx") : \r
450                         _eCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx") : wxEmptyString, & szCommandLine))\r
451         {\r
452                 g_LibInfo.node.GetAttribute(\r
453                         _eCommand == TPI_COMMAND_CREATE  ? wxT("create-alt") :\r
454                         _eCommand == TPI_COMMAND_ADD     ? wxT("add-alt") :\r
455                         _eCommand == TPI_COMMAND_EXTRACT ? wxT("extract-alt") : \r
456                         _eCommand == TPI_COMMAND_DELETE  ? wxT("delete-alt") : \r
457                         _eCommand == TPI_COMMAND_UPDATE  ? wxT("update-alt") : \r
458                         _eCommand == TPI_COMMAND_TEST    ? wxT("test-alt") : \r
459                         _eCommand == TPI_COMMAND_REPAIR  ? wxT("repair-alt") : \r
460                         _eCommand == TPI_COMMAND_MOVE    ? wxT("move-alt") : \r
461                         _eCommand == TPI_COMMAND_SFX     ? wxT("sfx-alt") : \r
462                         _eCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx-alt") : wxEmptyString, & szCommandLine);\r
463         }\r
464 \r
465         if (szCommandLine.IsEmpty())\r
466         {\r
467                 return TPI_ERROR_U_USE_LIBRARY;\r
468         }\r
469 \r
470         // コマンドライン・レスポンスファイル作成。\r
471         wxString\r
472                 szResponceFileName = MakeResponceFile(_szFiles, myGetAttributeBool(& g_LibInfo.node, wxT("quote-resp"), true)),\r
473                 szCommandLineSend  = MakeCommandLineSend(szCommandLine, _swInfo, _szFiles, szResponceFileName);\r
474 \r
475         // コマンドライン実行。\r
476         wxString szOutput;\r
477         int nErrorCode = myExecute(g_LibInfo.szExeFile + wxT(" ") + szCommandLineSend, & szOutput, _swInfo->fnDestinationDirectory.GetFullPath(), g_LibInfo.szExeFile.MakeLower().EndsWith(wxT(".exe")));\r
478 \r
479         // レスポンスファイル削除。\r
480         ::wxRemoveFile(szResponceFileName);\r
481 \r
482         if (nErrorCode != TPI_ERROR_SUCCESS)\r
483         {\r
484                 wxLogError(L"Error :\n%x\n\nCommandLine:\n%s\n\nOutput:\n%s", nErrorCode, szCommandLineSend.c_str(), szOutput.c_str());\r
485         }\r
486         return nErrorCode;\r
487 }\r
488 \r
489 #ifdef __cplusplus\r
490 }\r
491 #endif\r