1 /* SfxSetup.c - 7z SFX Setup
\r
2 2016-05-16 : Igor Pavlov : Public domain */
\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
25 #define k_EXE_ExtIndex 2
\r
27 static const char * const kExts[] =
\r
41 static const char * const kNames[] =
\r
49 static unsigned FindExt(const wchar_t *s, unsigned *extLen)
\r
51 unsigned len = (unsigned)wcslen(s);
\r
53 for (i = len; i > 0; i--)
\r
55 if (s[i - 1] == '.')
\r
65 #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c)))
\r
67 static unsigned FindItem(const char * const *items, unsigned num, const wchar_t *s, unsigned len)
\r
70 for (i = 0; i < num; i++)
\r
72 const char *item = items[i];
\r
73 unsigned itemLen = (unsigned)strlen(item);
\r
77 for (j = 0; j < len; j++)
\r
79 unsigned c = (Byte)item[j];
\r
80 if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j])
\r
90 static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
\r
92 UNUSED_VAR(ctrlType);
\r
97 static void PrintErrorMessage(const char *message)
\r
100 printf("\n7-Zip Error: %s\n", message);
\r
103 WCHAR messageW[256 + 4];
\r
105 for (i = 0; i < 256 && message[i] != 0; i++)
\r
106 messageW[i] = message[i];
\r
108 MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR);
\r
110 MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR);
\r
115 static WRes MyCreateDir(const WCHAR *name)
\r
117 return CreateDirectoryW(name, NULL) ? 0 : GetLastError();
\r
121 #define kBufferSize (1 << 13)
\r
123 #define kBufferSize (1 << 15)
\r
126 #define kSignatureSearchLimit (1 << 22)
\r
128 static Bool FindSignature(CSzFile *stream, UInt64 *resPos)
\r
130 Byte buf[kBufferSize];
\r
131 size_t numPrevBytes = 0;
\r
135 size_t processed, pos;
\r
136 if (*resPos > kSignatureSearchLimit)
\r
138 processed = kBufferSize - numPrevBytes;
\r
139 if (File_Read(stream, buf + numPrevBytes, &processed) != 0)
\r
141 processed += numPrevBytes;
\r
142 if (processed < k7zStartHeaderSize ||
\r
143 (processed == k7zStartHeaderSize && numPrevBytes != 0))
\r
145 processed -= k7zStartHeaderSize;
\r
146 for (pos = 0; pos <= processed; pos++)
\r
148 for (; pos <= processed && buf[pos] != '7'; pos++);
\r
149 if (pos > processed)
\r
151 if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0)
\r
152 if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8))
\r
158 *resPos += processed;
\r
159 numPrevBytes = k7zStartHeaderSize;
\r
160 memmove(buf, buf + processed, k7zStartHeaderSize);
\r
164 static Bool DoesFileOrDirExist(const WCHAR *path)
\r
166 WIN32_FIND_DATAW fd;
\r
168 handle = FindFirstFileW(path, &fd);
\r
169 if (handle == INVALID_HANDLE_VALUE)
\r
175 static WRes RemoveDirWithSubItems(WCHAR *path)
\r
177 WIN32_FIND_DATAW fd;
\r
180 size_t len = wcslen(path);
\r
181 wcscpy(path + len, L"*");
\r
182 handle = FindFirstFileW(path, &fd);
\r
184 if (handle == INVALID_HANDLE_VALUE)
\r
185 return GetLastError();
\r
189 if (wcscmp(fd.cFileName, L".") != 0 &&
\r
190 wcscmp(fd.cFileName, L"..") != 0)
\r
192 wcscpy(path + len, fd.cFileName);
\r
193 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
\r
195 wcscat(path, WSTRING_PATH_SEPARATOR);
\r
196 res = RemoveDirWithSubItems(path);
\r
200 SetFileAttributesW(path, 0);
\r
201 if (DeleteFileW(path) == 0)
\r
202 res = GetLastError();
\r
209 if (!FindNextFileW(handle, &fd))
\r
211 res = GetLastError();
\r
212 if (res == ERROR_NO_MORE_FILES)
\r
222 if (!RemoveDirectoryW(path))
\r
223 res = GetLastError();
\r
229 int MY_CDECL main()
\r
231 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
237 lpCmdLine, int nCmdShow)
\r
240 CFileInStream archiveStream;
\r
241 CLookToRead lookStream;
\r
245 ISzAlloc allocTempImp;
\r
246 WCHAR sfxPath[MAX_PATH + 2];
\r
247 WCHAR path[MAX_PATH * 3 + 2];
\r
249 WCHAR workCurDir[MAX_PATH + 32];
\r
253 const wchar_t *cmdLineParams;
\r
254 const char *errorMessage = NULL;
\r
255 Bool useShellExecute = True;
\r
256 DWORD exitCode = 0;
\r
258 LoadSecurityDlls();
\r
261 SetConsoleCtrlHandler(HandlerRoutine, TRUE);
\r
263 UNUSED_VAR(hInstance);
\r
264 UNUSED_VAR(hPrevInstance);
\r
265 UNUSED_VAR(lpCmdLine);
\r
266 UNUSED_VAR(nCmdShow);
\r
269 CrcGenerateTable();
\r
271 allocImp.Alloc = SzAlloc;
\r
272 allocImp.Free = SzFree;
\r
274 allocTempImp.Alloc = SzAllocTemp;
\r
275 allocTempImp.Free = SzFreeTemp;
\r
277 FileInStream_CreateVTable(&archiveStream);
\r
278 LookToRead_CreateVTable(&lookStream, False);
\r
280 winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
\r
281 if (winRes == 0 || winRes > MAX_PATH)
\r
284 cmdLineParams = GetCommandLineW();
\r
287 Bool quoteMode = False;
\r
288 for (;; cmdLineParams++)
\r
290 wchar_t c = *cmdLineParams;
\r
292 quoteMode = !quoteMode;
\r
293 else if (c == 0 || (c == L' ' && !quoteMode))
\r
303 winRes = GetTempPathW(MAX_PATH, path);
\r
304 if (winRes == 0 || winRes > MAX_PATH)
\r
306 pathLen = wcslen(path);
\r
307 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
\r
309 for (i = 0;; i++, d += GetTickCount())
\r
313 res = SZ_ERROR_FAIL;
\r
316 wcscpy(path + pathLen, L"7z");
\r
319 wchar_t *s = path + wcslen(path);
\r
322 for (k = 0; k < 8; k++)
\r
324 unsigned t = value & 0xF;
\r
326 s[7 - k] = (wchar_t)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
\r
331 if (DoesFileOrDirExist(path))
\r
333 if (CreateDirectoryW(path, NULL))
\r
335 wcscat(path, WSTRING_PATH_SEPARATOR);
\r
336 pathLen = wcslen(path);
\r
339 if (GetLastError() != ERROR_ALREADY_EXISTS)
\r
341 res = SZ_ERROR_FAIL;
\r
347 wcscpy(workCurDir, path);
\r
350 errorMessage = "Can't create temp folder";
\r
356 errorMessage = "Error";
\r
357 PrintErrorMessage(errorMessage);
\r
361 if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
\r
363 errorMessage = "can not open input file";
\r
364 res = SZ_ERROR_FAIL;
\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
374 errorMessage = "Can't find 7z archive";
\r
379 lookStream.realStream = &archiveStream.s;
\r
380 LookToRead_Init(&lookStream);
\r
386 res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
\r
391 UInt32 executeFileIndex = (UInt32)(Int32)-1;
\r
392 UInt32 minPrice = 1 << 30;
\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
398 for (i = 0; i < db.NumFiles; i++)
\r
401 size_t outSizeProcessed = 0;
\r
404 if (SzArEx_GetFileNameUtf16(&db, i, NULL) >= MAX_PATH)
\r
406 res = SZ_ERROR_FAIL;
\r
410 temp = path + pathLen;
\r
412 SzArEx_GetFileNameUtf16(&db, i, temp);
\r
414 res = SzArEx_Extract(&db, &lookStream.s, i,
\r
415 &blockIndex, &outBuffer, &outBufferSize,
\r
416 &offset, &outSizeProcessed,
\r
417 &allocImp, &allocTempImp);
\r
423 size_t processedSize;
\r
425 size_t nameStartPos = 0;
\r
426 for (j = 0; temp[j] != 0; j++)
\r
428 if (temp[j] == '/')
\r
432 temp[j] = CHAR_PATH_SEPARATOR;
\r
433 nameStartPos = j + 1;
\r
437 if (SzArEx_IsDir(&db, i))
\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
451 unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
\r
452 if (minPrice > price)
\r
455 executeFileIndex = i;
\r
456 useShellExecute = (extPrice != k_EXE_ExtIndex);
\r
459 if (DoesFileOrDirExist(path))
\r
461 errorMessage = "Duplicate file";
\r
462 res = SZ_ERROR_FAIL;
\r
465 if (OutFile_OpenW(&outFile, path))
\r
467 errorMessage = "Can't open output file";
\r
468 res = SZ_ERROR_FAIL;
\r
473 processedSize = outSizeProcessed;
\r
474 if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
\r
476 errorMessage = "Can't write output file";
\r
477 res = SZ_ERROR_FAIL;
\r
480 #ifdef USE_WINDOWS_FILE
\r
481 if (SzBitWithVals_Check(&db.MTime, i))
\r
483 const CNtfsFileTime *t = db.MTime.Vals + i;
\r
485 mTime.dwLowDateTime = t->Low;
\r
486 mTime.dwHighDateTime = t->High;
\r
487 SetFileTime(outFile.handle, NULL, NULL, &mTime);
\r
492 SRes res2 = File_Close(&outFile);
\r
501 #ifdef USE_WINDOWS_FILE
\r
502 if (SzBitWithVals_Check(&db.Attribs, i))
\r
503 SetFileAttributesW(path, db.Attribs.Vals[i]);
\r
510 if (executeFileIndex == (UInt32)(Int32)-1)
\r
512 errorMessage = "There is no file to execute";
\r
513 res = SZ_ERROR_FAIL;
\r
517 WCHAR *temp = path + pathLen;
\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
525 IAlloc_Free(&allocImp, outBuffer);
\r
527 SzArEx_Free(&db, &allocImp);
\r
529 File_Close(&archiveStream.file);
\r
533 HANDLE hProcess = 0;
\r
536 WCHAR oldCurDir[MAX_PATH + 2];
\r
539 DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);
\r
540 if (needLen == 0 || needLen > MAX_PATH)
\r
542 SetCurrentDirectory(workCurDir);
\r
546 if (useShellExecute)
\r
548 SHELLEXECUTEINFO ei;
\r
552 memset(&ei, 0, sizeof(ei));
\r
553 ei.cbSize = sizeof(ei);
\r
555 ei.fMask = SEE_MASK_NOCLOSEPROCESS
\r
557 | SEE_MASK_FLAG_DDEWAIT
\r
559 /* | SEE_MASK_NO_CONSOLE */
\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
569 hProcess = ei.hProcess;
\r
574 PROCESS_INFORMATION pi;
\r
575 WCHAR cmdLine[MAX_PATH * 3];
\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
585 CloseHandle(pi.hThread);
\r
586 hProcess = pi.hProcess;
\r
592 WaitForSingleObject(hProcess, INFINITE);
\r
593 if (!GetExitCodeProcess(hProcess, &exitCode))
\r
595 CloseHandle(hProcess);
\r
599 SetCurrentDirectory(oldCurDir);
\r
603 path[pathLen] = L'\0';
\r
604 RemoveDirWithSubItems(path);
\r
607 return (int)exitCode;
\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
619 errorMessage = "ERROR";
\r
623 PrintErrorMessage(errorMessage);
\r