Merge pull request #461 from negativeExponent/libchdr
[pcsx_rearmed.git] / deps / lzma-16.04 / C / Util / SfxSetup / SfxSetup.c
CommitLineData
ce188d4d 1/* SfxSetup.c - 7z SFX Setup\r
22016-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
27static 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
41static const char * const kNames[] =\r
42{\r
43 "setup"\r
44 , "install"\r
45 , "run"\r
46 , "start"\r
47};\r
48\r
49static 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
67static 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
90static BOOL WINAPI HandlerRoutine(DWORD ctrlType)\r
91{\r
92 UNUSED_VAR(ctrlType);\r
93 return TRUE;\r
94}\r
95#endif\r
96\r
97static 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
115static 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
128static 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
164static 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
175static 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
229int MY_CDECL main()\r
230#else\r
231int 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