OSDN Git Service

Merge with stable
[winmerge-jp/winmerge-jp.git] / Src / 7zCommon.cpp
1 /////////////////////////////////////////////////////////////////////////////\r
2 //    WinMerge:  an interactive diff/merge utility\r
3 //    Copyright (C) 1997-2000  Thingamahoochie Software\r
4 //    Author: Dean Grimm\r
5 //\r
6 //    This program is free software; you can redistribute it and/or modify\r
7 //    it under the terms of the GNU General Public License as published by\r
8 //    the Free Software Foundation; either version 2 of the License, or\r
9 //    (at your option) any later version.\r
10 //\r
11 //    This program is distributed in the hope that it will be useful,\r
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 //    GNU General Public License for more details.\r
15 //\r
16 //    You should have received a copy of the GNU General Public License\r
17 //    along with this program; if not, write to the Free Software\r
18 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
19 //\r
20 /////////////////////////////////////////////////////////////////////////////\r
21 \r
22 /* 7zCommon.cpp: Implement 7z related classes and functions\r
23  * Copyright (c) 2003 Jochen Tucht\r
24  *\r
25  * Remarks:     Different versions of 7-Zip are interfaced through specific\r
26  *                      versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.)\r
27  *                      WinMerge can either use an installed copy of the 7-Zip software,\r
28  *                      or fallback to a local set of 7-Zip DLLs, which are to be included\r
29  *                      in the WinMerge binary distribution.\r
30  *\r
31  *                      Fallback policies are as follows:\r
32  *\r
33  *                      1. Detect 7-Zip version installed (XXX).\r
34  *                      2. If there is a Merge7zXXX.dll, be happy to use it\r
35  *                      3. Detect 7-Zip version from WinMerge distribution (YYY).\r
36  *                      4. If there is a Merge7zYYY.dll, be happy to use it.\r
37  *                      5. Sorry, no way.\r
38  *\r
39  *                      These rules can be customized by setting a registry variable\r
40  *                      *Merge7z/Enable* of type DWORD to one of the following values:\r
41  *\r
42  *                      0 - Entirely disable 7-Zip integration.\r
43  *                      1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip.\r
44  *                      2 - Always use local 7-Zip.\r
45  *\r
46 \r
47 Please mind 2. a) of the GNU General Public License, and log your changes below.\r
48 \r
49 DATE:           BY:                                     DESCRIPTION:\r
50 ==========      ==================      ================================================\r
51 2003-12-09      Jochen Tucht            Created\r
52 2003-12-16      Jochen Tucht            Properly generate .tar.gz and .tar.bz2\r
53 2003-12-16      Jochen Tucht            Obtain long path to temporary folder\r
54 2004-01-20      Jochen Tucht            Complain only once if Merge7z*.dll is missing\r
55 2004-01-25      Jochen Tucht            Fix bad default for OPENFILENAME::nFilterIndex\r
56 2004-03-15      Jochen Tucht            Fix Visual Studio 2003 build issue\r
57 2004-04-13      Jochen Tucht            Avoid StrNCat to get away with shlwapi 4.70\r
58 2004-08-25      Jochen Tucht            More explicit error message\r
59 2004-10-17      Jochen Tucht            Leave decision whether to recurse into folders\r
60                                                                 to enumerator (Mask.Recurse)\r
61 2004-11-03      Jochen Tucht            FIX [1048997] as proposed by Kimmo 2004-11-02\r
62 2005-01-15      Jochen Tucht            Read 7-Zip version from 7zip_pad.xml\r
63                                                                 Set Merge7z UI language if DllBuild_Merge7z >= 9\r
64 2005-01-22      Jochen Tucht            Better explain what's present/missing/outdated\r
65 2005-02-05      Jochen Tucht            Fall back to IDD_MERGE7ZMISMATCH template from\r
66                                                                 .exe if .lang file isn't up to date.\r
67 2005-02-26      Jochen Tucht            Add download link to error message\r
68 2005-02-26      Jochen Tucht            Use WinAPI to obtain ISO language/region codes\r
69 2005-02-27      Jochen Tucht            FIX [1152375]\r
70 2005-04-24      Kimmo Varis                     Don't use DiffContext exported from DirView\r
71 2005-06-08      Kimmo Varis                     Use DIFFITEM, not reference to it (hopefully only\r
72                                                                 temporarily, to sort out new directory compare)\r
73 2005-06-22      Jochen Tucht            Change recommended version of 7-Zip to 4.20\r
74                                                                 Remove noise from Nagbox\r
75 2005-07-03      Jochen Tucht            DIFFITEM has changed due to RFE [ 1205516 ]\r
76 2005-07-04      Jochen Tucht            New global ArchiveGuessFormat() checks for\r
77                                                                 formats to be handled by external command line\r
78                                                                 tools. These take precedence over Merge7z\r
79                                                                 internal handlers.\r
80 2005-07-05      Jochen Tucht            Move to Merge7z::Format::GetDefaultName() to\r
81                                                                 build intermediate filenames for multi-step\r
82                                                                 compression.\r
83 2005-07-15      Jochen Tucht            Remove external command line tool integration\r
84                                                                 for now. Rethink about it after 2.4 branch.\r
85 2005-08-20      Jochen Tucht            Option to guess archive format by signature\r
86                                                                 Map extensions through ExternalArchiveFormat.ini\r
87 2005-08-23      Jochen Tucht            Option to entirely disable 7-Zip integration\r
88 2007-01-04      Kimmo Varis                     Convert using COptionsMgr for options.\r
89 2007-06-16      Jochen Neubeck          FIX [1723263] "Zip --> Both" operation...\r
90 2007-12-22      Jochen Neubeck          Fix Merge7z UI lang for new translation system\r
91                                                                 Change recommended version of 7-Zip to 4.57\r
92 2010-05-16      Jochen Neubeck          Read 7-Zip version from 7z.dll (which has long\r
93                                                                 ago replaced the various format and codec DLLs)\r
94                                                                 Change recommended version of 7-Zip to 4.65\r
95 */\r
96 \r
97 // ID line follows -- this is updated by SVN\r
98 // $Id: 7zCommon.cpp 7169 2010-05-16 14:44:19Z jtuc $\r
99 \r
100 #include "stdafx.h"\r
101 #include "7zCommon.h"\r
102 #include <afxinet.h>\r
103 #include <shlwapi.h>\r
104 #include "OptionsDef.h"\r
105 #include "OptionsMgr.h"\r
106 #include "Merge.h"              // DirDocFilter theApp GetOptionsMgr()\r
107 #include "resource.h"\r
108 #include "DirView.h"\r
109 #include "DirDoc.h"\r
110 #include "DirActions.h"\r
111 //#include "ExternalArchiveFormat.h"\r
112 #include "version.h"\r
113 #include "paths.h"\r
114 #include "Environment.h"\r
115 \r
116 #ifdef _DEBUG\r
117 #define new DEBUG_NEW\r
118 #undef THIS_FILE\r
119 static char THIS_FILE[] = __FILE__;\r
120 #endif\r
121 \r
122 /**\r
123  * @brief Proxy for Merge7z\r
124  */\r
125 static __declspec(thread) Merge7z::Proxy m_Merge7z =\r
126 {\r
127         { 0, 0, DllBuild_Merge7z, },\r
128         "Merge7z%u%02u"DECORATE_U".dll",\r
129         "Merge7z",\r
130         NULL\r
131 };\r
132 \r
133 /**\r
134  * @brief assign BSTR to String, and return BSTR for optional SysFreeString()\r
135  */\r
136 inline BSTR Assign(CString &dst, BSTR src)\r
137 {\r
138         dst = src;\r
139         return src;\r
140 }\r
141 \r
142 bool IsArchiveFile(const String& pszFile)\r
143 {\r
144         try {\r
145                 Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile);\r
146                 if (piHandler)\r
147                         return TRUE;\r
148                 else\r
149                         return FALSE;\r
150         }\r
151         catch (CException *e)\r
152         {\r
153                 e->Delete();\r
154                 return FALSE;\r
155         }\r
156         return FALSE;\r
157 }\r
158 \r
159 /**\r
160  * @brief Wrap Merge7z::GuessFormat() to allow for some customizing:\r
161  * - Check if 7-Zip integration is enabled.\r
162  * - Check for filename extension mappings.\r
163  */\r
164 Merge7z::Format *ArchiveGuessFormat(const String& path)\r
165 {\r
166         if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0)\r
167                 return NULL;\r
168         if (paths_IsDirectory(path))\r
169                 return NULL;\r
170         String path2(path);\r
171         // Map extensions through ExternalArchiveFormat.ini\r
172         static TCHAR null[] = _T("");\r
173         static const TCHAR section[] = _T("extensions");\r
174         String entry = paths_FindExtension(path);\r
175         TCHAR value[20];\r
176         static LPCTSTR filename = NULL;\r
177         if (filename == NULL)\r
178         {\r
179                 TCHAR cPath[INTERNET_MAX_PATH_LENGTH];\r
180                 DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL,\r
181                         INTERNET_MAX_PATH_LENGTH, cPath, NULL);\r
182                 filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null;\r
183         }\r
184         if (*filename &&\r
185                 GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) &&\r
186                 *value == '.')\r
187         {\r
188                 // Remove end-of-line comments (in string returned from GetPrivateProfileString)\r
189                 // that is, remove semicolon & whatever follows it\r
190                 if (LPTSTR p = StrChr(value, ';'))\r
191                 {\r
192                         *p = '\0';\r
193                         StrTrim(value, _T(" \t"));\r
194                 }\r
195                 path2 = value;\r
196         }\r
197 \r
198         // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues\r
199         // command line integration portion is not yet applied\r
200         // so following code not yet valid, so temporarily commented out\r
201         // Look for command line tool first\r
202         /*Merge7z::Format *pFormat;\r
203         if (CExternalArchiveFormat::GuessFormat(path, pFormat))\r
204         {\r
205                 return pFormat;\r
206         }*/\r
207         // Default to Merge7z*.dll\r
208 \r
209         return m_Merge7z->GuessFormat(path2.c_str());\r
210 }\r
211 \r
212 /**\r
213  * @brief Self-initializing raw C character buffer class.\r
214  */\r
215 template<class TYPE, size_t SIZE> struct CRawString\r
216 {\r
217         enum { Size = SIZE };\r
218         TYPE Data[SIZE];\r
219         CRawString()\r
220         {\r
221                 Data[0] = 0;\r
222         }\r
223 };\r
224 \r
225 /**\r
226  * @brief Exception class for more explicit error message.\r
227  */\r
228 class C7ZipMismatchException : public CException\r
229 {\r
230 public:\r
231         C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause)\r
232         {\r
233                 m_dwVer7zInstalled = dwVer7zInstalled;\r
234                 m_dwVer7zLocal = dwVer7zLocal;\r
235                 m_pCause = pCause;\r
236         }\r
237         ~C7ZipMismatchException()\r
238         {\r
239                 if (m_pCause)\r
240                         m_pCause->Delete();\r
241         }\r
242         virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);\r
243 protected:\r
244         DWORD m_dwVer7zInstalled;\r
245         DWORD m_dwVer7zLocal;\r
246         CException *m_pCause;\r
247         BOOL m_bShowAllways;\r
248         static const DWORD m_dwVer7zRecommended;\r
249         static const TCHAR m_strRegistryKey[];\r
250         static const TCHAR m_strDownloadURL[];\r
251         static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);\r
252         static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD);\r
253 };\r
254 \r
255 /**\r
256  * @brief Recommended version of 7-Zip.\r
257  */\r
258 const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4);\r
259 \r
260 /**\r
261  * @brief Registry key for C7ZipMismatchException's ReportError() popup.\r
262  */\r
263 const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch");\r
264 \r
265 /**\r
266  * @brief Download URL for C7ZipMismatchException's ReportError() popup.\r
267  */\r
268 const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957");\r
269 \r
270 /**\r
271  * @brief Retrieve build number of given DLL.\r
272  */\r
273 static DWORD NTAPI GetDllBuild(LPCTSTR cPath)\r
274 {\r
275         HMODULE hModule = LoadLibrary(cPath);\r
276         DLLVERSIONINFO dvi;\r
277         dvi.cbSize = sizeof dvi;\r
278         dvi.dwBuildNumber = ~0UL;\r
279         if (hModule)\r
280         {\r
281                 dvi.dwBuildNumber = 0UL;\r
282                 DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion");\r
283                 if (DllGetVersion)\r
284                 {\r
285                         DllGetVersion(&dvi);\r
286                 }\r
287                 FreeLibrary(hModule);\r
288         }\r
289         return dvi.dwBuildNumber;\r
290 }\r
291 \r
292 /**\r
293  * @brief Format Merge7z version number and plugin name, and retrieve DllBuild.\r
294  */\r
295 DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion)\r
296 {\r
297         *pcVersion = '\0';\r
298         *pcPluginName = '\0';\r
299         if (dwVersion)\r
300         {\r
301                 wsprintf\r
302                 (\r
303                         pcVersion, _T("%u.%02u"),\r
304                         UINT HIWORD(dwVersion),\r
305                         UINT LOWORD(dwVersion)\r
306                 );\r
307                 wsprintf\r
308                 (\r
309                         pcPluginName,\r
310                         sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"),\r
311                         UINT HIWORD(dwVersion),\r
312                         UINT LOWORD(dwVersion)\r
313                 );\r
314         }\r
315         return GetDllBuild(pcPluginName);\r
316 }\r
317 \r
318 /**\r
319  * @brief Populate ListBox with names/revisions of DLLs matching given pattern.\r
320  */\r
321 static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox)\r
322 {\r
323         HDC hDC = GetDC(hWnd);\r
324         HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0);\r
325         int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8;\r
326         HGDIOBJ hObject = SelectObject(hDC, hFont);\r
327         WIN32_FIND_DATA ff;\r
328         HANDLE h = FindFirstFile(cPattern, &ff);\r
329         if (h != INVALID_HANDLE_VALUE)\r
330         {\r
331                 do\r
332                 {\r
333                         PathRemoveFileSpec(cPattern);\r
334                         PathAppend(cPattern, ff.cFileName);\r
335                         wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern));\r
336                         lstrcat(cPattern, ff.cFileName);\r
337                         int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0);\r
338                         if (cxView < cxText)\r
339                                 cxView = cxText;\r
340                         SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern);\r
341                 } while (FindNextFile(h, &ff));\r
342                 FindClose(h);\r
343         }\r
344         SelectObject(hDC, hObject);\r
345         ReleaseDC(hWnd, hDC);\r
346         SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0);\r
347 }\r
348 \r
349 /**\r
350  * @brief OwnerDraw states from recent SDK.\r
351  */\r
352 #define ODS_NOACCEL         0x0100\r
353 #define ODS_NOFOCUSRECT     0x0200\r
354 \r
355 /**\r
356  * @brief WM_DRAWITEM notification handlers.\r
357  */\r
358 struct CDrawItemStruct : DRAWITEMSTRUCT\r
359 {\r
360         typedef CDrawItemStruct *From;\r
361         void DrawWebLinkButton();\r
362 };\r
363 \r
364 void CDrawItemStruct::DrawWebLinkButton()\r
365 {\r
366         CRawString<TCHAR,INTERNET_MAX_PATH_LENGTH> cText;\r
367         int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size);\r
368         COLORREF clrText = RGB(0,0,255);\r
369         if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT)\r
370         {\r
371                 clrText = RGB(128,0,128);\r
372         }\r
373         RECT rcText = rcItem;\r
374         ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT);\r
375         ::OffsetRect(&rcText, 1, 0);\r
376         rcItem.right = rcText.right + 1;\r
377         rcItem.bottom = rcText.bottom + 1;\r
378         switch (itemAction)\r
379         {\r
380         case ODA_DRAWENTIRE:\r
381                 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0);\r
382                 ::SetBkMode(hDC, TRANSPARENT);\r
383                 ::SetTextColor(hDC, clrText);\r
384                 ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT);\r
385                 rcText.top = rcText.bottom - 1;\r
386                 ::SetBkColor(hDC, clrText);\r
387                 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);\r
388                 if (itemState & ODS_FOCUS)\r
389                 {\r
390                 case ODA_FOCUS:\r
391                         if (!(itemState & ODS_NOFOCUSRECT))\r
392                         {\r
393                                 ::SetTextColor(hDC, 0);\r
394                                 ::SetBkColor(hDC, RGB(255,255,255));\r
395                                 ::SetBkMode(hDC, OPAQUE);\r
396                                 DrawFocusRect(hDC, &rcItem);\r
397                         }\r
398                 }\r
399                 break;\r
400         }\r
401 }\r
402 \r
403 /**\r
404  * @brief Load a cursor from COMCTL32.DLL.\r
405  */\r
406 HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName)\r
407 {\r
408         HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL"));\r
409         return hModule ? LoadCursor(hModule, lpCursorName) : NULL;\r
410 }\r
411 \r
412 /**\r
413  * @brief DLGPROC for C7ZipMismatchException's ReportError() popup.\r
414  */\r
415 INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
416 {\r
417         switch (uMsg)\r
418         {\r
419                 case WM_INITDIALOG:\r
420                 {\r
421                         theApp.TranslateDialog(hWnd);\r
422                         if (GetDlgItem(hWnd, 9001) == NULL)\r
423                         {\r
424                                 // Dialog template isn't up to date. Give it a second chance.\r
425                                 EndDialog(hWnd, -1);\r
426                                 return FALSE;\r
427                         }\r
428                         C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam;\r
429                         CRawString<TCHAR,2600> cText;\r
430                         CRawString<TCHAR,80> cPresent, cMissing, cOutdated, cNone, cPlugin;\r
431                         if (pThis->m_pCause)\r
432                         {\r
433                                 pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size);\r
434                                 SetDlgItemText(hWnd, 107, cText.Data);\r
435                         }\r
436                         else\r
437                         {\r
438                                 GetDlgItemText(hWnd, 107, cText.Data, cText.Size);\r
439                                 switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
440                                 {\r
441                                 case 0:\r
442                                         lstrcat(cText.Data, _("\nNote: 7-Zip integration is disabled in WinMerge settings.").c_str());\r
443                                         break;\r
444                                 case 2:\r
445                                         lstrcat(cText.Data, _("\nNote: 7-Zip integration is restricted to standalone operation in WinMerge settings.").c_str());\r
446                                         break;\r
447                                 }\r
448                                 SetDlgItemText(hWnd, 107, cText.Data);\r
449                         }\r
450                         GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size);\r
451                         GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size);\r
452                         GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size);\r
453                         GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size);\r
454                         GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size);\r
455                         wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z);\r
456                         SetDlgItemText(hWnd, 102, cText.Data);\r
457                         SetDlgItemText\r
458                         (\r
459                                 hWnd, 109,\r
460                                 (\r
461                                         pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled\r
462                                 ||      pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal\r
463                                 ) ? cPresent.Data : cMissing.Data\r
464                         );\r
465                         DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended);\r
466                         SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data);\r
467                         SetDlgItemText(hWnd, 111, cPlugin.Data);\r
468                         SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data :\r
469                                 dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
470                         dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled);\r
471                         SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data);\r
472                         SetDlgItemText(hWnd, 121, cPlugin.Data);\r
473                         SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data :\r
474                                 dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
475                         dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal);\r
476                         SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data);\r
477                         SetDlgItemText(hWnd, 131, cPlugin.Data);\r
478                         SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data :\r
479                                 dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
480                         GetModuleFileName(0, cText.Data, MAX_PATH);\r
481                         PathRemoveFileSpec(cText.Data);\r
482                         PathAppend(cText.Data, _T("Merge7z*.dll"));\r
483                         DlgDirListDLLs(hWnd, cText.Data, 105);\r
484                         if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0))\r
485                         {\r
486                                 static const TCHAR cSep[] = _T(";");\r
487                                 LPTSTR pchPath = new TCHAR[cchPath];\r
488                                 GetEnvironmentVariable(_T("PATH"), pchPath, cchPath);\r
489                                 LPTSTR pchItem = pchPath;\r
490                                 while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep))\r
491                                 {\r
492                                         if (cchItem < MAX_PATH)\r
493                                         {\r
494                                                 CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem);\r
495                                                 cText.Data[cchItem] = 0;\r
496                                                 PathAppend(cText.Data, _T("Merge7z*.dll"));\r
497                                                 DlgDirListDLLs(hWnd, cText.Data, 105);\r
498                                         }\r
499                                         pchItem += cchItem;\r
500                                 }\r
501                                 delete[] pchPath;\r
502                         }\r
503                         if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0)\r
504                         {\r
505                                 SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data);\r
506                         }\r
507                         HICON hIcon = LoadIcon(0, IDI_EXCLAMATION);\r
508                         SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);\r
509                         SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);\r
510                         if (pThis->m_bShowAllways)\r
511                         {\r
512                                 ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE);\r
513                         }\r
514                 } return TRUE;\r
515                 case WM_DRAWITEM:\r
516                 {\r
517                         switch (wParam)\r
518                         {\r
519                         case 108:\r
520                                 CDrawItemStruct::From(lParam)->DrawWebLinkButton();\r
521                                 break;\r
522                         }\r
523                 } return TRUE;\r
524                 case WM_SETCURSOR:\r
525                 {\r
526                         HCURSOR hCursor = 0;\r
527                         switch (GetDlgCtrlID((HWND)wParam))\r
528                         {\r
529                         case 108:\r
530                                 hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108));\r
531                                 break;\r
532                         }\r
533                         if (hCursor)\r
534                         {\r
535                                 SetCursor(hCursor);\r
536                                 SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1);\r
537                                 return TRUE;\r
538                         }\r
539                 } return FALSE;\r
540                 case WM_COMMAND:\r
541                 {\r
542                         switch (wParam)\r
543                         {\r
544                                 case IDOK:\r
545                                 case IDCANCEL:\r
546                                 {\r
547                                         LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0);\r
548                                         EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain));\r
549                                 } break;\r
550                                 case 108:\r
551                                 {\r
552                                         HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL);\r
553                                         if ((UINT)h > 32)\r
554                                         {\r
555                                                 LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE);\r
556                                                 ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT);\r
557                                                 ::InvalidateRect((HWND)lParam, 0, TRUE);\r
558                                         }\r
559                                         else\r
560                                         {\r
561                                                 MessageBeep(0);\r
562                                         }\r
563                                 } break;\r
564                         }\r
565                 } return TRUE;\r
566         }\r
567         return FALSE;\r
568 }\r
569 \r
570 /**\r
571  * @brief Tell user what went wrong and how she can help.\r
572  */\r
573 int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID)\r
574 {\r
575         UINT_PTR response = -1;\r
576         m_bShowAllways = nMessageID;\r
577         if (!m_bShowAllways)\r
578         {\r
579                 // Suppress error message in case 7-Zip is not installed.\r
580                 response =\r
581                 (\r
582                         m_dwVer7zInstalled || m_dwVer7zLocal\r
583                 ?       theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1)\r
584                 :       IDOK\r
585                 );\r
586         }\r
587         if (response == -1)\r
588         {\r
589                 HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();\r
590                 response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);\r
591                 if (response == -1)\r
592                 {\r
593                         response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);\r
594                         ASSERT(response != -1);\r
595                 }\r
596                 if (HIBYTE(response) == 1)\r
597                 {\r
598                         theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response));\r
599                 }\r
600         }\r
601         return response;\r
602 }\r
603 \r
604 /**\r
605  * @brief Check whether archive support is available.\r
606  */\r
607 int NTAPI HasZipSupport()\r
608 {\r
609         static int HasZipSupport = -1;\r
610         if (HasZipSupport == -1)\r
611         {\r
612                 try\r
613                 {\r
614                         m_Merge7z.operator->();\r
615                         HasZipSupport = 1;\r
616                 }\r
617                 catch (CException *e)\r
618                 {\r
619                         e->Delete();\r
620                         HasZipSupport = 0;\r
621                 }\r
622         }\r
623         return HasZipSupport;\r
624 }\r
625 \r
626 /**\r
627  * @brief Tell user why archive support is not available.\r
628  */\r
629 void NTAPI Recall7ZipMismatchError()\r
630 {\r
631         try\r
632         {\r
633                 m_Merge7z.operator->();\r
634         }\r
635         catch (CException *e)\r
636         {\r
637                 e->ReportError(MB_ICONEXCLAMATION, TRUE);\r
638                 e->Delete();\r
639         }\r
640 }\r
641 \r
642 /**\r
643  * @brief Delete head of temp path context list, and return its parent context.\r
644  */\r
645 CTempPathContext *CTempPathContext::DeleteHead()\r
646 {\r
647         CTempPathContext *pParent = m_pParent;\r
648         delete this;\r
649         return pParent;\r
650 }\r
651 \r
652 BOOL NTAPI IsMerge7zEnabled()\r
653 {\r
654         return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0);\r
655 }\r
656 \r
657 /**\r
658  * @brief Return installed or local version of 7-Zip.\r
659  */\r
660 DWORD NTAPI VersionOf7z(BOOL bLocal)\r
661 {\r
662         TCHAR path[MAX_PATH];\r
663         if (bLocal)\r
664         {\r
665                 GetModuleFileName(0, path, sizeof path/sizeof*path);\r
666                 PathRemoveFileSpec(path);\r
667         }\r
668         else\r
669         {\r
670                 static const TCHAR szSubKey[] = _T("Software\\7-Zip");\r
671                 static const TCHAR szValue[] = _T("Path");\r
672                 DWORD type = 0;\r
673                 DWORD size = sizeof path;\r
674                 SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size);\r
675         }\r
676         PathAppend(path, _T("7z.dll"));\r
677         unsigned versionMS = 0;\r
678         unsigned versionLS = 0;\r
679         CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS);\r
680         return versionMS;\r
681 }\r
682 \r
683 /**\r
684  * @brief Access dll functions through proxy.\r
685  */\r
686 interface Merge7z *Merge7z::Proxy::operator->()\r
687 {\r
688         // As long as the Merge7z*.DLL has not yet been loaded, Merge7z\r
689         // [0] points to the name of the DLL (with placeholders for 7-\r
690         // Zip major and minor version numbers). Once the DLL has been\r
691         // loaded successfully, Merge7z[0] is set to NULL, causing the\r
692         // if to fail on subsequent calls.\r
693 \r
694         if (const char *format = Merge7z[0])\r
695         {\r
696                 // Merge7z has not yet been loaded\r
697 \r
698                 char name[MAX_PATH];\r
699                 DWORD flags = ~0;\r
700                 CException *pCause = NULL;\r
701                 switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
702                 {\r
703                 case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip.\r
704                         if (DWORD ver = VersionOf7z(FALSE))\r
705                         {\r
706                                 flags = Initialize::Default;\r
707                                 try\r
708                                 {\r
709                                         wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
710                                         Merge7z[0] = name;\r
711                                         stub.Load();\r
712                                         break;\r
713                                 }\r
714                                 catch (CException *e)\r
715                                 {\r
716                                         Merge7z[0] = format;\r
717                                         pCause = e;\r
718                                 }\r
719                         }\r
720                 case 2: //Always use local 7-Zip.\r
721                         if (DWORD ver = VersionOf7z(TRUE))\r
722                         {\r
723                                 flags = Initialize::Default | Initialize::Local7z;\r
724                                 try\r
725                                 {\r
726                                         wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
727                                         Merge7z[0] = name;\r
728                                         stub.Load();\r
729                                         break;\r
730                                 }\r
731                                 catch (CException *e)\r
732                                 {\r
733                                         Merge7z[0] = format;\r
734                                         if (pCause) pCause->Delete();\r
735                                         pCause = e;\r
736                                 }\r
737                         }\r
738                 default:\r
739                         throw new C7ZipMismatchException\r
740                         (\r
741                                 VersionOf7z(FALSE),\r
742                                 VersionOf7z(TRUE),\r
743                                 pCause\r
744                         );\r
745                 }\r
746                 LANGID wLangID = (LANGID)GetThreadLocale();\r
747                 flags |= wLangID << 16;\r
748                 if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))\r
749                 {\r
750                         flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;\r
751                 }\r
752                 if (Merge7z[1])\r
753                         ((interface Merge7z *)Merge7z[1])->Initialize(flags);\r
754         }\r
755         return ((interface Merge7z *)Merge7z[1]);\r
756 }\r
757 \r
758 /**\r
759  * @brief Tell Merge7z we are going to enumerate just 1 item.\r
760  */\r
761 UINT SingleItemEnumerator::Open()\r
762 {\r
763         return 1;\r
764 }\r
765 \r
766 /**\r
767  * @brief Pass information about the item to Merge7z.\r
768  */\r
769 Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)\r
770 {\r
771         item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;\r
772         item.Name = Name;\r
773         item.FullPath = FullPath;\r
774         return 0;\r
775 }\r
776 \r
777 /**\r
778  * @brief SingleFileEnumerator constructor.\r
779  */\r
780 SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name)\r
781 : FullPath(FullPath)\r
782 , Name(Name)\r
783 {\r
784 }\r
785 \r
786 /**\r
787  * @brief Construct a DirItemEnumerator.\r
788  *\r
789  * Argument *nFlags* controls operation as follows:\r
790  * LVNI_ALL:            Enumerate all items.\r
791  * LVNI_SELECTED:       Enumerate selected items only.\r
792  * Original:            Set folder prefix for first iteration to "original"\r
793  * Altered:                     Set folder prefix for second iteration to "altered"\r
794  * BalanceFolders:      Ensure that all nonempty folders on either side have a\r
795  *                                      corresponding folder on the other side, even if it is\r
796  *                                      empty (DirScan doesn't recurse into folders which\r
797  *                                      appear only on one side).\r
798  * DiffsOnly:           Enumerate diffs only.\r
799  */\r
800 DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)\r
801 : m_pView(pView)\r
802 , m_nFlags(nFlags)\r
803 {\r
804         if (m_nFlags & Original)\r
805         {\r
806                 m_rgFolderPrefix.push_back(_T("original"));\r
807         }\r
808         if (m_nFlags & Altered)\r
809         {\r
810                 m_rgFolderPrefix.push_back(_T("altered"));\r
811         }\r
812         if (m_nFlags & BalanceFolders)\r
813         {\r
814                 const CDiffContext& ctxt = pView->GetDiffContext();\r
815                 // Collect implied folders\r
816                 for (UINT i = Open() ; i-- ; )\r
817                 {\r
818                         const DIFFITEM &di = Next();\r
819                         if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))\r
820                         {\r
821                                 continue;\r
822                         }\r
823                         // Enumerating items\r
824                         if (di.diffcode.isExists(m_index))\r
825                         {\r
826                                 // Item is present on right side, i.e. folder is implied\r
827                                 m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1);\r
828                         }\r
829                 }\r
830         }\r
831 }\r
832 \r
833 /**\r
834  * @brief Initialize enumerator, return number of items to be enumerated.\r
835  */\r
836 UINT DirItemEnumerator::Open()\r
837 {\r
838         m_nIndex = -1;\r
839         m_curFolderPrefix = m_rgFolderPrefix.begin();\r
840         m_index = (m_nFlags & Right) != 0 ? 1 : 0;\r
841         size_t nrgFolderPrefix = m_rgFolderPrefix.size();\r
842         if (nrgFolderPrefix)\r
843         {\r
844                 m_strFolderPrefix = *m_curFolderPrefix++;\r
845         }\r
846         else\r
847         {\r
848                 nrgFolderPrefix = 1;\r
849         }\r
850         return\r
851         (\r
852                 m_nFlags & LVNI_SELECTED\r
853         ?       pView(m_pView)->GetSelectedCount()\r
854         :       pView(m_pView)->GetItemCount()\r
855         ) * nrgFolderPrefix;\r
856 }\r
857 \r
858 /**\r
859  * @brief Return next item.\r
860  */\r
861 const DIFFITEM &DirItemEnumerator::Next()\r
862 {\r
863         enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};\r
864         while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)\r
865         {\r
866                 m_strFolderPrefix = *m_curFolderPrefix++;\r
867                 m_index = 1;\r
868         }\r
869         return m_pView->GetDiffItem(m_nIndex);\r
870 }\r
871 \r
872 /**\r
873  * @brief Pass information about an item to Merge7z.\r
874  *\r
875  * Information is passed through struct Merge7z::DirItemEnumerator::Item.\r
876  * The *mask* member denotes which of the other members contain valid data.\r
877  * If *mask* is zero upon return, which will be the case if Enum() decides to\r
878  * leave the struct untouched, Merge7z will ignore the item.\r
879  * If Enum() allocates temporary storage for string members, it must also\r
880  * allocate an Envelope, providing a Free() method to free the temporary\r
881  * storage, along with the Envelope itself. The Envelope pointer is passed to\r
882  * Merge7z as the return value of the function. It is not meant to be a success\r
883  * indicator, so if no temporary storage is required, it is perfectly alright\r
884  * to return NULL.\r
885  */\r
886 Merge7z::Envelope *DirItemEnumerator::Enum(Item &item)\r
887 {\r
888         const CDiffContext& ctxt = m_pView->GetDiffContext();\r
889         const DIFFITEM &di = Next();\r
890 \r
891         if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))\r
892         {\r
893                 return 0;\r
894         }\r
895 \r
896         bool isSideOnly = !di.diffcode.isExists(m_index);\r
897 \r
898         Envelope *envelope = new Envelope;\r
899 \r
900         const String &sFilename = di.diffFileInfo[m_index].filename;\r
901         const String &sSubdir = di.diffFileInfo[m_index].path;\r
902         if (sSubdir.length())\r
903                 envelope->Name = paths_ConcatPath(sSubdir, sFilename);\r
904         else\r
905                 envelope->Name = sFilename;\r
906         envelope->FullPath = paths_ConcatPath(\r
907                         di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)),\r
908                         sFilename);\r
909 \r
910         UINT32 Recurse = item.Mask.Recurse;\r
911 \r
912         if (m_nFlags & BalanceFolders)\r
913         {\r
914                 // Enumerating items on right side\r
915                 if (isSideOnly)\r
916                 {\r
917                         // Item is missing on right side\r
918                         PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()];\r
919                         if (!implied)\r
920                         {\r
921                                 // Folder is not implied by some other file, and has\r
922                                 // not been enumerated so far, so enumerate it now!\r
923                                 envelope->Name = di.diffFileInfo[1-m_index].path;\r
924                                 envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index));\r
925                                 implied = PVOID(2); // Don't enumerate same folder twice!\r
926                                 isSideOnly = false;\r
927                                 Recurse = 0;\r
928                         }\r
929                 }\r
930         }\r
931 \r
932         if (isSideOnly)\r
933         {\r
934                 return envelope;\r
935         }\r
936 \r
937         if (m_strFolderPrefix.length())\r
938         {\r
939                 if (envelope->Name.length())\r
940                         envelope->Name.insert(0, _T("\\"));\r
941                 envelope->Name.insert(0, m_strFolderPrefix);\r
942         }\r
943 \r
944         item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;\r
945         item.Name = envelope->Name.c_str();\r
946         item.FullPath = envelope->FullPath.c_str();\r
947         return envelope;\r
948 }\r
949 \r
950 /**\r
951  * @brief Apply appropriate handlers from left to right.\r
952  */\r
953 bool DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path)\r
954 {\r
955         DeleteFile(path);\r
956         Merge7z::Format *piHandler = ArchiveGuessFormat(path);\r
957         if (piHandler)\r
958         {\r
959                 HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();\r
960                 CString pathIntermediate;\r
961                 SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));\r
962                 String pathPrepend = path;\r
963                 pathPrepend.resize(pathPrepend.rfind('\\') + 1);\r
964                 pathIntermediate.Insert(0, pathPrepend.c_str());\r
965                 bool bDone = MultiStepCompressArchive(pathIntermediate);\r
966                 if (bDone)\r
967                 {\r
968                         piHandler->CompressArchive(hwndOwner, path,\r
969                                 &SingleItemEnumerator(path, pathIntermediate));\r
970                         DeleteFile(pathIntermediate);\r
971                 }\r
972                 else\r
973                 {\r
974                         piHandler->CompressArchive(hwndOwner, path, this);\r
975                 }\r
976                 return true;\r
977         }\r
978         return false;\r
979 }\r
980 \r
981 /**\r
982  * @brief Generate archive from DirView items.\r
983  */\r
984 void DirItemEnumerator::CompressArchive(LPCTSTR path)\r
985 {\r
986         String strPath;\r
987         if (path == 0)\r
988         {\r
989                 // No path given, so prompt for path!\r
990                 static const TCHAR _T_Merge7z[] = _T("Merge7z");\r
991                 static const TCHAR _T_FilterIndex[] = _T("FilterIndex");\r
992                 // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't\r
993                 // offer other formats here!\r
994                 static const TCHAR _T_Filter[]\r
995                 (\r
996                         _T("7z|*.7z|")\r
997                         //_T("z|*.z|")\r
998                         _T("zip|*.zip|")\r
999                         _T("jar (zip)|*.jar|")\r
1000                         _T("ear (zip)|*.ear|")\r
1001                         _T("war (zip)|*.war|")\r
1002                         _T("xpi (zip)|*.xpi|")\r
1003                         //_T("rar|*.rar|")\r
1004                         _T("tar|*.tar|")\r
1005                         _T("tar.z|*.tar.z|")\r
1006                         _T("tar.gz|*.tar.gz|")\r
1007                         _T("tar.bz2|*.tar.bz2|")\r
1008                         //_T("tz|*.tz|")\r
1009                         _T("tgz|*.tgz|")\r
1010                         _T("tbz2|*.tbz2|")\r
1011                         //_T("lzh|*.lzh|")\r
1012                         //_T("cab|*.cab|")\r
1013                         //_T("arj|*.arj|")\r
1014                         //_T("deb|*.deb|")\r
1015                         //_T("rpm|*.rpm|")\r
1016                         //_T("cpio|*.cpio|")\r
1017                         //_T("|")\r
1018                 );\r
1019                 String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();\r
1020                 strFilter.insert(0, _T_Filter);\r
1021                 strFilter += _T("|");\r
1022                 CFileDialog dlg\r
1023                 (\r
1024                         FALSE,\r
1025                         0,\r
1026                         0,\r
1027                         OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,\r
1028                         strFilter.c_str()\r
1029                 );\r
1030                 dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1);\r
1031                 // Use extension from current filter as default extension:\r
1032                 if (int i = dlg.m_ofn.nFilterIndex)\r
1033                 {\r
1034                         dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;\r
1035                         while (*dlg.m_ofn.lpstrDefExt && --i)\r
1036                         {\r
1037                                 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;\r
1038                                 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;\r
1039                         }\r
1040                         if (*dlg.m_ofn.lpstrDefExt)\r
1041                         {\r
1042                                 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;\r
1043                         }\r
1044                 }\r
1045                 if (dlg.DoModal() == IDOK)\r
1046                 {\r
1047                         strPath = dlg.GetPathName();\r
1048                         path = strPath.c_str();\r
1049                         AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex);\r
1050                 }\r
1051         }\r
1052         if (path && !MultiStepCompressArchive(path))\r
1053         {\r
1054                 LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);\r
1055         }\r
1056 }\r