Sync to latest upstream
[pcsx_rearmed.git] / deps / lzma-16.04 / C / Util / SfxSetup / SfxSetup.c
1 /* SfxSetup.c - 7z SFX Setup\r
2 2016-05-16 : Igor Pavlov : Public domain */\r
3 \r
4 #include "Precomp.h"\r
5 \r
6 #ifndef UNICODE\r
7 #define UNICODE\r
8 #endif\r
9 \r
10 #ifndef _UNICODE\r
11 #define _UNICODE\r
12 #endif\r
13 \r
14 #ifdef _CONSOLE\r
15 #include <stdio.h>\r
16 #endif\r
17 \r
18 #include "../../7z.h"\r
19 #include "../../7zAlloc.h"\r
20 #include "../../7zCrc.h"\r
21 #include "../../7zFile.h"\r
22 #include "../../CpuArch.h"\r
23 #include "../../DllSecur.h"\r
24 \r
25 #define k_EXE_ExtIndex 2\r
26 \r
27 static const char * const kExts[] =\r
28 {\r
29     "bat"\r
30   , "cmd"\r
31   , "exe"\r
32   , "inf"\r
33   , "msi"\r
34   #ifdef UNDER_CE\r
35   , "cab"\r
36   #endif\r
37   , "html"\r
38   , "htm"\r
39 };\r
40 \r
41 static const char * const kNames[] =\r
42 {\r
43     "setup"\r
44   , "install"\r
45   , "run"\r
46   , "start"\r
47 };\r
48 \r
49 static unsigned FindExt(const wchar_t *s, unsigned *extLen)\r
50 {\r
51   unsigned len = (unsigned)wcslen(s);\r
52   unsigned i;\r
53   for (i = len; i > 0; i--)\r
54   {\r
55     if (s[i - 1] == '.')\r
56     {\r
57       *extLen = len - i;\r
58       return i - 1;\r
59     }\r
60   }\r
61   *extLen = 0;\r
62   return len;\r
63 }\r
64 \r
65 #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c)))\r
66 \r
67 static unsigned FindItem(const char * const *items, unsigned num, const wchar_t *s, unsigned len)\r
68 {\r
69   unsigned i;\r
70   for (i = 0; i < num; i++)\r
71   {\r
72     const char *item = items[i];\r
73     unsigned itemLen = (unsigned)strlen(item);\r
74     unsigned j;\r
75     if (len != itemLen)\r
76       continue;\r
77     for (j = 0; j < len; j++)\r
78     {\r
79       unsigned c = (Byte)item[j];\r
80       if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j])\r
81         break;\r
82     }\r
83     if (j == len)\r
84       return i;\r
85   }\r
86   return i;\r
87 }\r
88 \r
89 #ifdef _CONSOLE\r
90 static BOOL WINAPI HandlerRoutine(DWORD ctrlType)\r
91 {\r
92   UNUSED_VAR(ctrlType);\r
93   return TRUE;\r
94 }\r
95 #endif\r
96 \r
97 static void PrintErrorMessage(const char *message)\r
98 {\r
99   #ifdef _CONSOLE\r
100   printf("\n7-Zip Error: %s\n", message);\r
101   #else\r
102   #ifdef UNDER_CE\r
103   WCHAR messageW[256 + 4];\r
104   unsigned i;\r
105   for (i = 0; i < 256 && message[i] != 0; i++)\r
106     messageW[i] = message[i];\r
107   messageW[i] = 0;\r
108   MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR);\r
109   #else\r
110   MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR);\r
111   #endif\r
112   #endif\r
113 }\r
114 \r
115 static WRes MyCreateDir(const WCHAR *name)\r
116 {\r
117   return CreateDirectoryW(name, NULL) ? 0 : GetLastError();\r
118 }\r
119 \r
120 #ifdef UNDER_CE\r
121 #define kBufferSize (1 << 13)\r
122 #else\r
123 #define kBufferSize (1 << 15)\r
124 #endif\r
125 \r
126 #define kSignatureSearchLimit (1 << 22)\r
127 \r
128 static Bool FindSignature(CSzFile *stream, UInt64 *resPos)\r
129 {\r
130   Byte buf[kBufferSize];\r
131   size_t numPrevBytes = 0;\r
132   *resPos = 0;\r
133   for (;;)\r
134   {\r
135     size_t processed, pos;\r
136     if (*resPos > kSignatureSearchLimit)\r
137       return False;\r
138     processed = kBufferSize - numPrevBytes;\r
139     if (File_Read(stream, buf + numPrevBytes, &processed) != 0)\r
140       return False;\r
141     processed += numPrevBytes;\r
142     if (processed < k7zStartHeaderSize ||\r
143         (processed == k7zStartHeaderSize && numPrevBytes != 0))\r
144       return False;\r
145     processed -= k7zStartHeaderSize;\r
146     for (pos = 0; pos <= processed; pos++)\r
147     {\r
148       for (; pos <= processed && buf[pos] != '7'; pos++);\r
149       if (pos > processed)\r
150         break;\r
151       if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0)\r
152         if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8))\r
153         {\r
154           *resPos += pos;\r
155           return True;\r
156         }\r
157     }\r
158     *resPos += processed;\r
159     numPrevBytes = k7zStartHeaderSize;\r
160     memmove(buf, buf + processed, k7zStartHeaderSize);\r
161   }\r
162 }\r
163 \r
164 static Bool DoesFileOrDirExist(const WCHAR *path)\r
165 {\r
166   WIN32_FIND_DATAW fd;\r
167   HANDLE handle;\r
168   handle = FindFirstFileW(path, &fd);\r
169   if (handle == INVALID_HANDLE_VALUE)\r
170     return False;\r
171   FindClose(handle);\r
172   return True;\r
173 }\r
174 \r
175 static WRes RemoveDirWithSubItems(WCHAR *path)\r
176 {\r
177   WIN32_FIND_DATAW fd;\r
178   HANDLE handle;\r
179   WRes res = 0;\r
180   size_t len = wcslen(path);\r
181   wcscpy(path + len, L"*");\r
182   handle = FindFirstFileW(path, &fd);\r
183   path[len] = L'\0';\r
184   if (handle == INVALID_HANDLE_VALUE)\r
185     return GetLastError();\r
186   \r
187   for (;;)\r
188   {\r
189     if (wcscmp(fd.cFileName, L".") != 0 &&\r
190         wcscmp(fd.cFileName, L"..") != 0)\r
191     {\r
192       wcscpy(path + len, fd.cFileName);\r
193       if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)\r
194       {\r
195         wcscat(path, WSTRING_PATH_SEPARATOR);\r
196         res = RemoveDirWithSubItems(path);\r
197       }\r
198       else\r
199       {\r
200         SetFileAttributesW(path, 0);\r
201         if (DeleteFileW(path) == 0)\r
202           res = GetLastError();\r
203       }\r
204     \r
205       if (res != 0)\r
206         break;\r
207     }\r
208   \r
209     if (!FindNextFileW(handle, &fd))\r
210     {\r
211       res = GetLastError();\r
212       if (res == ERROR_NO_MORE_FILES)\r
213         res = 0;\r
214       break;\r
215     }\r
216   }\r
217   \r
218   path[len] = L'\0';\r
219   FindClose(handle);\r
220   if (res == 0)\r
221   {\r
222     if (!RemoveDirectoryW(path))\r
223       res = GetLastError();\r
224   }\r
225   return res;\r
226 }\r
227 \r
228 #ifdef _CONSOLE\r
229 int MY_CDECL main()\r
230 #else\r
231 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
232   #ifdef UNDER_CE\r
233   LPWSTR\r
234   #else\r
235   LPSTR\r
236   #endif\r
237   lpCmdLine, int nCmdShow)\r
238 #endif\r
239 {\r
240   CFileInStream archiveStream;\r
241   CLookToRead lookStream;\r
242   CSzArEx db;\r
243   SRes res = SZ_OK;\r
244   ISzAlloc allocImp;\r
245   ISzAlloc allocTempImp;\r
246   WCHAR sfxPath[MAX_PATH + 2];\r
247   WCHAR path[MAX_PATH * 3 + 2];\r
248   #ifndef UNDER_CE\r
249   WCHAR workCurDir[MAX_PATH + 32];\r
250   #endif\r
251   size_t pathLen;\r
252   DWORD winRes;\r
253   const wchar_t *cmdLineParams;\r
254   const char *errorMessage = NULL;\r
255   Bool useShellExecute = True;\r
256   DWORD exitCode = 0;\r
257 \r
258   LoadSecurityDlls();\r
259 \r
260   #ifdef _CONSOLE\r
261   SetConsoleCtrlHandler(HandlerRoutine, TRUE);\r
262   #else\r
263   UNUSED_VAR(hInstance);\r
264   UNUSED_VAR(hPrevInstance);\r
265   UNUSED_VAR(lpCmdLine);\r
266   UNUSED_VAR(nCmdShow);\r
267   #endif\r
268 \r
269   CrcGenerateTable();\r
270 \r
271   allocImp.Alloc = SzAlloc;\r
272   allocImp.Free = SzFree;\r
273 \r
274   allocTempImp.Alloc = SzAllocTemp;\r
275   allocTempImp.Free = SzFreeTemp;\r
276 \r
277   FileInStream_CreateVTable(&archiveStream);\r
278   LookToRead_CreateVTable(&lookStream, False);\r
279  \r
280   winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);\r
281   if (winRes == 0 || winRes > MAX_PATH)\r
282     return 1;\r
283   {\r
284     cmdLineParams = GetCommandLineW();\r
285     #ifndef UNDER_CE\r
286     {\r
287       Bool quoteMode = False;\r
288       for (;; cmdLineParams++)\r
289       {\r
290         wchar_t c = *cmdLineParams;\r
291         if (c == L'\"')\r
292           quoteMode = !quoteMode;\r
293         else if (c == 0 || (c == L' ' && !quoteMode))\r
294           break;\r
295       }\r
296     }\r
297     #endif\r
298   }\r
299 \r
300   {\r
301     unsigned i;\r
302     DWORD d;\r
303     winRes = GetTempPathW(MAX_PATH, path);\r
304     if (winRes == 0 || winRes > MAX_PATH)\r
305       return 1;\r
306     pathLen = wcslen(path);\r
307     d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();\r
308     \r
309     for (i = 0;; i++, d += GetTickCount())\r
310     {\r
311       if (i >= 100)\r
312       {\r
313         res = SZ_ERROR_FAIL;\r
314         break;\r
315       }\r
316       wcscpy(path + pathLen, L"7z");\r
317 \r
318       {\r
319         wchar_t *s = path + wcslen(path);\r
320         UInt32 value = d;\r
321         unsigned k;\r
322         for (k = 0; k < 8; k++)\r
323         {\r
324           unsigned t = value & 0xF;\r
325           value >>= 4;\r
326           s[7 - k] = (wchar_t)((t < 10) ? ('0' + t) : ('A' + (t - 10)));\r
327         }\r
328         s[k] = '\0';\r
329       }\r
330 \r
331       if (DoesFileOrDirExist(path))\r
332         continue;\r
333       if (CreateDirectoryW(path, NULL))\r
334       {\r
335         wcscat(path, WSTRING_PATH_SEPARATOR);\r
336         pathLen = wcslen(path);\r
337         break;\r
338       }\r
339       if (GetLastError() != ERROR_ALREADY_EXISTS)\r
340       {\r
341         res = SZ_ERROR_FAIL;\r
342         break;\r
343       }\r
344     }\r
345     \r
346     #ifndef UNDER_CE\r
347     wcscpy(workCurDir, path);\r
348     #endif\r
349     if (res != SZ_OK)\r
350       errorMessage = "Can't create temp folder";\r
351   }\r
352 \r
353   if (res != SZ_OK)\r
354   {\r
355     if (!errorMessage)\r
356       errorMessage = "Error";\r
357     PrintErrorMessage(errorMessage);\r
358     return 1;\r
359   }\r
360 \r
361   if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)\r
362   {\r
363     errorMessage = "can not open input file";\r
364     res = SZ_ERROR_FAIL;\r
365   }\r
366   else\r
367   {\r
368     UInt64 pos = 0;\r
369     if (!FindSignature(&archiveStream.file, &pos))\r
370       res = SZ_ERROR_FAIL;\r
371     else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)\r
372       res = SZ_ERROR_FAIL;\r
373     if (res != 0)\r
374       errorMessage = "Can't find 7z archive";\r
375   }\r
376 \r
377   if (res == SZ_OK)\r
378   {\r
379     lookStream.realStream = &archiveStream.s;\r
380     LookToRead_Init(&lookStream);\r
381   }\r
382 \r
383   SzArEx_Init(&db);\r
384   if (res == SZ_OK)\r
385   {\r
386     res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);\r
387   }\r
388   \r
389   if (res == SZ_OK)\r
390   {\r
391     UInt32 executeFileIndex = (UInt32)(Int32)-1;\r
392     UInt32 minPrice = 1 << 30;\r
393     UInt32 i;\r
394     UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */\r
395     Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */\r
396     size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */\r
397     \r
398     for (i = 0; i < db.NumFiles; i++)\r
399     {\r
400       size_t offset = 0;\r
401       size_t outSizeProcessed = 0;\r
402       WCHAR *temp;\r
403 \r
404       if (SzArEx_GetFileNameUtf16(&db, i, NULL) >= MAX_PATH)\r
405       {\r
406         res = SZ_ERROR_FAIL;\r
407         break;\r
408       }\r
409       \r
410       temp = path + pathLen;\r
411       \r
412       SzArEx_GetFileNameUtf16(&db, i, temp);\r
413       {\r
414         res = SzArEx_Extract(&db, &lookStream.s, i,\r
415           &blockIndex, &outBuffer, &outBufferSize,\r
416           &offset, &outSizeProcessed,\r
417           &allocImp, &allocTempImp);\r
418         if (res != SZ_OK)\r
419           break;\r
420       }\r
421       {\r
422         CSzFile outFile;\r
423         size_t processedSize;\r
424         size_t j;\r
425         size_t nameStartPos = 0;\r
426         for (j = 0; temp[j] != 0; j++)\r
427         {\r
428           if (temp[j] == '/')\r
429           {\r
430             temp[j] = 0;\r
431             MyCreateDir(path);\r
432             temp[j] = CHAR_PATH_SEPARATOR;\r
433             nameStartPos = j + 1;\r
434           }\r
435         }\r
436 \r
437         if (SzArEx_IsDir(&db, i))\r
438         {\r
439           MyCreateDir(path);\r
440           continue;\r
441         }\r
442         else\r
443         {\r
444           unsigned extLen;\r
445           const WCHAR *name = temp + nameStartPos;\r
446           unsigned len = (unsigned)wcslen(name);\r
447           unsigned nameLen = FindExt(temp + nameStartPos, &extLen);\r
448           unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);\r
449           unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);\r
450 \r
451           unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));\r
452           if (minPrice > price)\r
453           {\r
454             minPrice = price;\r
455             executeFileIndex = i;\r
456             useShellExecute = (extPrice != k_EXE_ExtIndex);\r
457           }\r
458          \r
459           if (DoesFileOrDirExist(path))\r
460           {\r
461             errorMessage = "Duplicate file";\r
462             res = SZ_ERROR_FAIL;\r
463             break;\r
464           }\r
465           if (OutFile_OpenW(&outFile, path))\r
466           {\r
467             errorMessage = "Can't open output file";\r
468             res = SZ_ERROR_FAIL;\r
469             break;\r
470           }\r
471         }\r
472   \r
473         processedSize = outSizeProcessed;\r
474         if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)\r
475         {\r
476           errorMessage = "Can't write output file";\r
477           res = SZ_ERROR_FAIL;\r
478         }\r
479         \r
480         #ifdef USE_WINDOWS_FILE\r
481         if (SzBitWithVals_Check(&db.MTime, i))\r
482         {\r
483           const CNtfsFileTime *t = db.MTime.Vals + i;\r
484           FILETIME mTime;\r
485           mTime.dwLowDateTime = t->Low;\r
486           mTime.dwHighDateTime = t->High;\r
487           SetFileTime(outFile.handle, NULL, NULL, &mTime);\r
488         }\r
489         #endif\r
490         \r
491         {\r
492           SRes res2 = File_Close(&outFile);\r
493           if (res != SZ_OK)\r
494             break;\r
495           if (res2 != SZ_OK)\r
496           {\r
497             res = res2;\r
498             break;\r
499           }\r
500         }\r
501         #ifdef USE_WINDOWS_FILE\r
502         if (SzBitWithVals_Check(&db.Attribs, i))\r
503           SetFileAttributesW(path, db.Attribs.Vals[i]);\r
504         #endif\r
505       }\r
506     }\r
507 \r
508     if (res == SZ_OK)\r
509     {\r
510       if (executeFileIndex == (UInt32)(Int32)-1)\r
511       {\r
512         errorMessage = "There is no file to execute";\r
513         res = SZ_ERROR_FAIL;\r
514       }\r
515       else\r
516       {\r
517         WCHAR *temp = path + pathLen;\r
518         UInt32 j;\r
519         SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);\r
520         for (j = 0; temp[j] != 0; j++)\r
521           if (temp[j] == '/')\r
522             temp[j] = CHAR_PATH_SEPARATOR;\r
523       }\r
524     }\r
525     IAlloc_Free(&allocImp, outBuffer);\r
526   }\r
527   SzArEx_Free(&db, &allocImp);\r
528 \r
529   File_Close(&archiveStream.file);\r
530 \r
531   if (res == SZ_OK)\r
532   {\r
533     HANDLE hProcess = 0;\r
534     \r
535     #ifndef UNDER_CE\r
536     WCHAR oldCurDir[MAX_PATH + 2];\r
537     oldCurDir[0] = 0;\r
538     {\r
539       DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);\r
540       if (needLen == 0 || needLen > MAX_PATH)\r
541         oldCurDir[0] = 0;\r
542       SetCurrentDirectory(workCurDir);\r
543     }\r
544     #endif\r
545     \r
546     if (useShellExecute)\r
547     {\r
548       SHELLEXECUTEINFO ei;\r
549       UINT32 executeRes;\r
550       BOOL success;\r
551       \r
552       memset(&ei, 0, sizeof(ei));\r
553       ei.cbSize = sizeof(ei);\r
554       ei.lpFile = path;\r
555       ei.fMask = SEE_MASK_NOCLOSEPROCESS\r
556           #ifndef UNDER_CE\r
557           | SEE_MASK_FLAG_DDEWAIT\r
558           #endif\r
559           /* | SEE_MASK_NO_CONSOLE */\r
560           ;\r
561       if (wcslen(cmdLineParams) != 0)\r
562         ei.lpParameters = cmdLineParams;\r
563       ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */\r
564       success = ShellExecuteEx(&ei);\r
565       executeRes = (UINT32)(UINT_PTR)ei.hInstApp;\r
566       if (!success || (executeRes <= 32 && executeRes != 0))  /* executeRes = 0 in Windows CE */\r
567         res = SZ_ERROR_FAIL;\r
568       else\r
569         hProcess = ei.hProcess;\r
570     }\r
571     else\r
572     {\r
573       STARTUPINFOW si;\r
574       PROCESS_INFORMATION pi;\r
575       WCHAR cmdLine[MAX_PATH * 3];\r
576 \r
577       wcscpy(cmdLine, path);\r
578       wcscat(cmdLine, cmdLineParams);\r
579       memset(&si, 0, sizeof(si));\r
580       si.cb = sizeof(si);\r
581       if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)\r
582         res = SZ_ERROR_FAIL;\r
583       else\r
584       {\r
585         CloseHandle(pi.hThread);\r
586         hProcess = pi.hProcess;\r
587       }\r
588     }\r
589     \r
590     if (hProcess != 0)\r
591     {\r
592       WaitForSingleObject(hProcess, INFINITE);\r
593       if (!GetExitCodeProcess(hProcess, &exitCode))\r
594         exitCode = 1;\r
595       CloseHandle(hProcess);\r
596     }\r
597     \r
598     #ifndef UNDER_CE\r
599     SetCurrentDirectory(oldCurDir);\r
600     #endif\r
601   }\r
602 \r
603   path[pathLen] = L'\0';\r
604   RemoveDirWithSubItems(path);\r
605 \r
606   if (res == SZ_OK)\r
607     return (int)exitCode;\r
608   \r
609   {\r
610     if (res == SZ_ERROR_UNSUPPORTED)\r
611       errorMessage = "Decoder doesn't support this archive";\r
612     else if (res == SZ_ERROR_MEM)\r
613       errorMessage = "Can't allocate required memory";\r
614     else if (res == SZ_ERROR_CRC)\r
615       errorMessage = "CRC error";\r
616     else\r
617     {\r
618       if (!errorMessage)\r
619         errorMessage = "ERROR";\r
620     }\r
621  \r
622     if (errorMessage)\r
623       PrintErrorMessage(errorMessage);\r
624   }\r
625   return 1;\r
626 }\r