OSDN Git Service

7f6ef37cdfa17d0247f319e0468e63c2baa523c1
[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 #include "../../common/header/plugin.h"\r
27 #include "../../common/header/plugin-extra.h"\r
28 #include "../../common/library/library.h"\r
29 #include <wx/config.h>\r
30 #include <wx/stdpaths.h>\r
31 #include <wx/xml/xml.h>\r
32 #include <wx/tokenzr.h>\r
33 #include "cuiWrapper.h"\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, 0))\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                 char sz[32768];\r
108                 while (true)\r
109                 {\r
110                         DWORD dwSize = 0;\r
111                         if (! ::PeekNamedPipe(hRead, NULL, 0, NULL, & dwSize, NULL))\r
112                         {\r
113                                 continue;\r
114                         }\r
115 \r
116                         if (dwSize > 0)\r
117                         {\r
118                                 memset(sz, 0, sizeof(sz));\r
119                                 ::ReadFile(hRead, & sz, sizeof(sz), & dwSize, NULL);\r
120                                 * szOutput += UTF82String(sz);\r
121 //                              ::MessageBoxA(NULL, sz, NULL, 0);\r
122                         }\r
123                         else if (::WaitForSingleObject(pi.hProcess, 0) == WAIT_OBJECT_0)\r
124                         {\r
125                                 break;\r
126                         }\r
127                 }\r
128         }\r
129 \r
130         ::CloseHandle(pi.hProcess);\r
131         ::CloseHandle(hRead);\r
132         ::CloseHandle(hWrite);\r
133         return TPI_ERROR_SUCCESS;\r
134 #endif\r
135 }\r
136 \r
137 PosInfo MakePosInfo(const wxString & szPrefix)\r
138 {\r
139         PosInfo pi;\r
140         g_LibInfo.node.GetAttribute(wxT("list-") + szPrefix + wxT("-s"), wxEmptyString).ToULongLong(& pi.nStart);\r
141         g_LibInfo.node.GetAttribute(wxT("list-") + szPrefix + wxT("-c"), wxEmptyString).ToULongLong(& pi.nCount);\r
142         g_LibInfo.node.GetAttribute(wxT("list-") + szPrefix + wxT("-l"), wxT("0")).ToULongLong(& pi.nLine);\r
143         return pi;\r
144 }\r
145 \r
146 wxULongLong_t GetSize(const PosInfo & pi, wxULongLong_t nCurrent, const wxArrayString & as)\r
147 {\r
148         if (pi.nStart == 0 && pi.nCount == 0)\r
149         {\r
150                 return 0;\r
151         }\r
152 \r
153         wxULongLong_t nTemp = 0, nPos = nCurrent + pi.nLine;\r
154         wxString sz = pi.nCount == 0 ? as[nPos].Mid(pi.nStart) : as[nPos].Mid(pi.nStart, pi.nCount);\r
155         sz.ToULongLong(& nTemp);\r
156         return nTemp;\r
157 }\r
158 \r
159 //******************************************************************************\r
160 //    Functions\r
161 //******************************************************************************\r
162 \r
163 #ifdef __cplusplus\r
164 extern "C"\r
165 {\r
166 #endif\r
167 \r
168 int __stdcall GetPluginInformation\r
169 (\r
170         unsigned int _uInfoId,\r
171         wxULongLong_t,\r
172         void * _pPtr\r
173 )\r
174 {\r
175         if (_pPtr == NULL)\r
176         {\r
177                 return TPI_ERROR_D_PARAMETER;\r
178         }\r
179         switch (_uInfoId)\r
180         {\r
181         case TPI_INFO_VERSION_MAJOR:\r
182         case TPI_INFO_VERSION_MINOR:\r
183                 * (int *) _pPtr = 0;\r
184                 break;\r
185         case TPI_INFO_VERSION_API:\r
186                 * (int *) _pPtr = 2;\r
187                 break;\r
188         default:\r
189                 return TPI_ERROR_D_UNSUPPORTED;\r
190         }\r
191         return TPI_ERROR_SUCCESS;\r
192 }\r
193 \r
194 int __stdcall GetFormatInformation(TPI_FORMATINFO * _fiInfo, bool _bFirst)\r
195 {\r
196         static wxULongLong_t s_nFileId;\r
197         wxStandardPaths p;\r
198         wxXmlDocument config(wxPathOnly(p.GetExecutablePath()) + wxT("/lib/cuiWrapper.xml"));\r
199         // 一気に先頭のライブラリの情報を取得。\r
200         wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();\r
201 \r
202         if (_bFirst)\r
203         {\r
204                 // xml解析開始。\r
205                 s_nFileId = 0;\r
206         }\r
207         else\r
208         {\r
209                 for (wxULongLong_t i = 0; i < s_nFileId && xmlLibrary != NULL; i++)\r
210                 {\r
211                         xmlLibrary = xmlLibrary->GetNext();\r
212                 }\r
213         }\r
214 \r
215         if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))\r
216         {\r
217                 // データの終端に達した場合。\r
218                 return TPI_ERROR_S_ENDOFDATA;\r
219         }\r
220 \r
221         MakeFormatInfo(wxT("cuiWrapper"), _fiInfo, xmlLibrary, s_nFileId++);\r
222         wxString szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
223         if (myExecute(szExeFile, NULL, wxEmptyString, szExeFile.Find(wxT('.')) != wxNOT_FOUND && szExeFile.AfterLast(wxT('.')) == wxT("exe")) != TPI_ERROR_SUCCESS)\r
224         {\r
225                 _fiInfo->eSupportedCommand = 0;\r
226         }\r
227 \r
228         return TPI_ERROR_SUCCESS;\r
229 }\r
230 \r
231 int __stdcall LoadPlugin\r
232 (\r
233         const wxString & _szArcName,\r
234         wxULongLong_t _llTypeId\r
235 )\r
236 {\r
237         // xml解析開始。\r
238         wxStandardPaths p;\r
239         wxXmlDocument config(wxPathOnly(p.GetExecutablePath()) + wxT("/lib/cuiWrapper.xml"));\r
240         if (! config.IsOk())\r
241         {\r
242                 return TPI_ERROR_UNDEFINED;\r
243         }\r
244         // 一気に先頭のライブラリの情報を取得。\r
245         wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();\r
246 \r
247         // 対象が存在するならば対応するライブラリを調査、\r
248         // 対象が存在しないならば指示されたライブラリをロード。\r
249         if (! ::wxFileExists(_szArcName))\r
250         {\r
251                 // 適当な位置まで移動。\r
252                 for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < _llTypeId && xmlLibrary != NULL; g_LibInfo.nLibIndex++)\r
253                 {\r
254                         xmlLibrary = xmlLibrary->GetNext();\r
255                 }\r
256                 if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))\r
257                 {\r
258                         // xml文法エラー。\r
259                         return TPI_ERROR_UNDEFINED;\r
260                 }\r
261                 g_LibInfo.szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
262                 g_LibInfo.szExeFileAlt = xmlLibrary->GetAttribute(wxT("name-alt"), wxEmptyString);\r
263                 g_LibInfo.node = * xmlLibrary;\r
264 \r
265                 return TPI_ERROR_SUCCESS;\r
266         }\r
267 \r
268         // 無限ループに陥らないよう上限を設定。\r
269         for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < 300 && xmlLibrary != NULL; g_LibInfo.nLibIndex++)\r
270         {\r
271                 // ライブラリをロード。\r
272                 g_LibInfo.szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
273                 g_LibInfo.szExeFileAlt = xmlLibrary->GetAttribute(wxT("name-alt"), wxEmptyString);\r
274                 g_LibInfo.node = * xmlLibrary;\r
275 \r
276                 // 書庫に対応しているかチェック。\r
277                 wxFileName fnArchive(_szArcName);\r
278                 wxArrayString asExt = ::wxStringTokenize(g_LibInfo.node.GetAttribute(wxT("suffix"), wxEmptyString), wxT(";"));\r
279                 if (! g_LibInfo.node.HasAttribute(wxT("list")))\r
280                 {\r
281                         xmlLibrary = xmlLibrary->GetNext();\r
282                         continue;\r
283                 }\r
284 \r
285                 for (size_t i = 0; i < asExt.GetCount(); i++)\r
286                 {\r
287                         // .tar.XXXなど二重判定への対応。\r
288 //                      if (asExt[i].IsSameAs(fnArchive.GetExt(), false))\r
289                         if (fnArchive.GetFullName().EndsWith(wxT('.') + asExt[i]))\r
290                         {\r
291                                 return TPI_ERROR_SUCCESS;\r
292                         }\r
293                 }\r
294 \r
295                 xmlLibrary = xmlLibrary->GetNext();\r
296         }\r
297 \r
298         return TPI_ERROR_U_LOAD_LIBRARY;\r
299 }\r
300 \r
301 int __stdcall FreePlugin\r
302 (\r
303         void * // _pReserved\r
304 )\r
305 {\r
306         return TPI_ERROR_SUCCESS;\r
307 }\r
308 \r
309 int __stdcall OpenArchive\r
310 (\r
311         const wxString & _szArcName,\r
312         void * * _hArchive,\r
313         wxULongLong_t * _nFileCount\r
314 )\r
315 {\r
316         wxString szOutput;\r
317         if (myExecute(g_LibInfo.szExeFile + wxT(" ") + MakeCommandLineSend(g_LibInfo.node.GetAttribute(wxT("list"), wxEmptyString), _szArcName), & szOutput, wxEmptyString, g_LibInfo.szExeFile.Find(wxT('.')) != wxNOT_FOUND && g_LibInfo.szExeFile.AfterLast(wxT('.')) == wxT("exe")) != TPI_ERROR_SUCCESS)\r
318         {\r
319                 return TPI_ERROR_U_USE_LIBRARY;\r
320         }\r
321         wxArrayString * as = new wxArrayString(::wxStringTokenize(szOutput, wxT("\r\n")));\r
322         * _hArchive = (void *) as;\r
323         as->Shrink();\r
324         if (_nFileCount != NULL)\r
325         {\r
326                 * _nFileCount = as->GetCount();\r
327         }\r
328         return as->IsEmpty() ? TPI_ERROR_UNDEFINED : TPI_ERROR_SUCCESS;\r
329 }\r
330 \r
331 int __stdcall CloseArchive\r
332 (\r
333         void * _hArchive\r
334 )\r
335 {\r
336         wxArrayString * as = (wxArrayString *) _hArchive;\r
337         as->Clear();\r
338         delete as;\r
339         return TPI_ERROR_SUCCESS;\r
340 }\r
341 \r
342 int __stdcall GetFileInformation\r
343 (\r
344         void * _hArchive,\r
345         TPI_FILEINFO * _fiInfo,\r
346         bool _bFirst\r
347 )\r
348 {\r
349         static size_t s_nCurrentLine;\r
350         static wxULongLong_t s_nFileId;\r
351         if (_hArchive == NULL)\r
352         {\r
353                 return TPI_ERROR_UNDEFINED;\r
354         }\r
355         wxArrayString asOutput = * (wxArrayString *) _hArchive;\r
356 \r
357         // XMLからの読み込みは初回に行う。\r
358         static wxString szEndLine, szDateFormat;\r
359         static wxULongLong_t nProcessPerLine;\r
360         static PosInfo piFName, piPSize, piUSize, piDate;\r
361         if (_bFirst)\r
362         {\r
363                 s_nFileId = 0;\r
364                 wxString szStartLine = g_LibInfo.node.GetAttribute(wxT("list-line-s"), wxEmptyString);\r
365                 if (! szStartLine.IsEmpty())\r
366                 {\r
367                         // 開始行の次の行にセット。\r
368                         s_nCurrentLine = asOutput.Index(szStartLine) + 1;\r
369                         if (s_nCurrentLine == wxNOT_FOUND + 1)\r
370                         {\r
371                                 // 書庫が読み込めなかった?\r
372                                 return TPI_ERROR_ARC_UNSUPPORTED;\r
373                         }\r
374                 }\r
375 \r
376                 // 初期設定。\r
377                 g_LibInfo.node.GetAttribute(wxT("list-line-c"), wxT("1")).ToULongLong(& nProcessPerLine);\r
378                 szEndLine = g_LibInfo.node.GetAttribute(wxT("list-line-e"), szStartLine);\r
379                 szDateFormat = g_LibInfo.node.GetAttribute(wxT("list-date-f"), wxDefaultDateTimeFormat);\r
380                 piFName = MakePosInfo(wxT("fname"));\r
381                 piPSize = MakePosInfo(wxT("psize"));\r
382                 piUSize = MakePosInfo(wxT("usize"));\r
383                 piDate  = MakePosInfo(wxT("date"));\r
384         }\r
385 \r
386         if (s_nCurrentLine >= asOutput.GetCount())\r
387         {\r
388                 // 空行で終わるとき以外はエラーとする。\r
389                 return szEndLine.IsEmpty() ? TPI_ERROR_S_ENDOFDATA : TPI_ERROR_ARC_UNSUPPORTED;\r
390         }\r
391 \r
392         // 最終行かどうか確認。\r
393         if (asOutput[s_nCurrentLine] == szEndLine)\r
394         {\r
395                 return TPI_ERROR_S_ENDOFDATA;\r
396         }\r
397 \r
398         // ファイル名を取得。\r
399         _fiInfo->szStoredName = piFName.nCount == 0 ? asOutput[s_nCurrentLine + piFName.nLine].Mid(piFName.nStart) : asOutput[s_nCurrentLine + piFName.nLine].Mid(piFName.nStart, piFName.nCount);\r
400         _fiInfo->szStoredName.Trim();\r
401         _fiInfo->fnFileName = wxFileName(_fiInfo->szStoredName, wxPATH_DOS);\r
402 \r
403         // サイズ取得。\r
404         _fiInfo->nPackedSize   = GetSize(piPSize, s_nCurrentLine, asOutput);\r
405         _fiInfo->nUnpackedSize = GetSize(piUSize, s_nCurrentLine, asOutput);\r
406 \r
407         // 更新時刻取得。\r
408         if (piDate.nStart != 0 || piDate.nCount != 0)\r
409         {\r
410                 _fiInfo->tmModified.ParseFormat(piDate.nCount == 0 ? asOutput[s_nCurrentLine + piDate.nLine].Mid(piDate.nStart) : asOutput[s_nCurrentLine + piDate.nLine].Mid(piDate.nStart, piDate.nCount), szDateFormat);\r
411         }\r
412 \r
413         // 最後に次の行へ進めておく。\r
414         _fiInfo->nFileId = s_nFileId++;\r
415         s_nCurrentLine += nProcessPerLine;\r
416 \r
417         return TPI_ERROR_SUCCESS;\r
418 }\r
419 \r
420 int __stdcall GetArchiveInformation\r
421 (\r
422         void *,\r
423         TPI_ARCHIVEINFO * _aiInfo\r
424 )\r
425 {\r
426         // 形式に関する情報を取得。\r
427         MakeFormatInfo(wxT("cuiWrapper"), & _aiInfo->fiInfo, & g_LibInfo.node, 0);\r
428         return TPI_ERROR_SUCCESS;\r
429 }\r
430 \r
431 int __stdcall Command\r
432 (\r
433         wxULongLong_t _eCommand,\r
434         TPI_SWITCHES * _swInfo,\r
435         const wxString & _szArcName,\r
436         const wxArrayString & _szFiles\r
437 )\r
438 {\r
439         // xmlからコマンドラインを取得。\r
440         wxString szPath, szCommandLine;\r
441 \r
442         // APIアドレス取得。\r
443         if (! g_LibInfo.node.GetAttribute(\r
444                         _eCommand == TPI_COMMAND_CREATE  ? wxT("create") :\r
445                         _eCommand == TPI_COMMAND_ADD     ? wxT("add") :\r
446                         _eCommand == TPI_COMMAND_EXTRACT ? wxT("extract") : \r
447                         _eCommand == TPI_COMMAND_DELETE  ? wxT("delete") : \r
448                         _eCommand == TPI_COMMAND_UPDATE  ? wxT("update") : \r
449                         _eCommand == TPI_COMMAND_TEST    ? wxT("test") : \r
450                         _eCommand == TPI_COMMAND_REPAIR  ? wxT("repair") : \r
451                         _eCommand == TPI_COMMAND_MOVE    ? wxT("move") : \r
452                         _eCommand == TPI_COMMAND_SFX     ? wxT("sfx") : \r
453                         _eCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx") : wxEmptyString, & szCommandLine))\r
454         {\r
455                 g_LibInfo.node.GetAttribute(\r
456                         _eCommand == TPI_COMMAND_CREATE  ? wxT("create-alt") :\r
457                         _eCommand == TPI_COMMAND_ADD     ? wxT("add-alt") :\r
458                         _eCommand == TPI_COMMAND_EXTRACT ? wxT("extract-alt") : \r
459                         _eCommand == TPI_COMMAND_DELETE  ? wxT("delete-alt") : \r
460                         _eCommand == TPI_COMMAND_UPDATE  ? wxT("update-alt") : \r
461                         _eCommand == TPI_COMMAND_TEST    ? wxT("test-alt") : \r
462                         _eCommand == TPI_COMMAND_REPAIR  ? wxT("repair-alt") : \r
463                         _eCommand == TPI_COMMAND_MOVE    ? wxT("move-alt") : \r
464                         _eCommand == TPI_COMMAND_SFX     ? wxT("sfx-alt") : \r
465                         _eCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx-alt") : wxEmptyString, & szCommandLine);\r
466         }\r
467 \r
468         if (szCommandLine.IsEmpty())\r
469         {\r
470                 return TPI_ERROR_U_USE_LIBRARY;\r
471         }\r
472 \r
473         // コマンドライン・レスポンスファイル作成。\r
474         wxString\r
475                 szResponceFileName = MakeResponceFile(_szFiles, g_LibInfo.node.GetAttribute(wxT("quote-resp"), wxT("1")) == wxT("1")),\r
476                 szCommandLineSend  = MakeCommandLineSend(szCommandLine, _szArcName, _swInfo, _szFiles, szResponceFileName);\r
477 \r
478         // コマンドライン実行。\r
479         wxString szOutput;\r
480         int nErrorCode = myExecute(g_LibInfo.szExeFile + wxT(" ") + szCommandLineSend, & szOutput, _swInfo->fnDestinationDirectory.GetFullPath(), g_LibInfo.szExeFile.Find(wxT('.')) != wxNOT_FOUND && g_LibInfo.szExeFile.AfterLast(wxT('.')) == wxT("exe"));\r
481 \r
482         // レスポンスファイル削除。\r
483         ::wxRemoveFile(szResponceFileName);\r
484 \r
485         if (nErrorCode != TPI_ERROR_SUCCESS)\r
486         {\r
487                 wxLogError(L"Error :\n%x\n\nCommandLine:\n%s\n\nOutput:\n%s", nErrorCode, szCommandLineSend.c_str(), szOutput.c_str());\r
488         }\r
489         return nErrorCode;\r
490 }\r
491 \r
492 int __stdcall SetCallbackProc\r
493 (\r
494         TPI_PROC _prArcProc\r
495 )\r
496 {\r
497         // ポインタを保存。\r
498         if (_prArcProc == NULL)\r
499         {\r
500                 return TPI_ERROR_D_PARAMETER;\r
501         }\r
502         g_prProc = * _prArcProc;\r
503 \r
504         return TPI_ERROR_SUCCESS;\r
505 }\r
506 \r
507 #ifdef __cplusplus\r
508 }\r
509 #endif\r