1 /* SfxSetup.c - 7z SFX Setup
\r
2 2010-11-11 : Igor Pavlov : Public domain */
\r
16 #include "../../7z.h"
\r
17 #include "../../7zAlloc.h"
\r
18 #include "../../7zCrc.h"
\r
19 #include "../../7zFile.h"
\r
20 #include "../../CpuArch.h"
\r
22 #define k_EXE_ExtIndex 1
\r
24 static const char *kExts[] =
\r
37 static const char *kNames[] =
\r
45 static unsigned FindExt(const wchar_t *s, unsigned *extLen)
\r
47 unsigned len = (unsigned)wcslen(s);
\r
49 for (i = len; i > 0; i--)
\r
51 if (s[i - 1] == '.')
\r
61 #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c)))
\r
63 static unsigned FindItem(const char **items, unsigned num, const wchar_t *s, unsigned len)
\r
66 for (i = 0; i < num; i++)
\r
68 const char *item = items[i];
\r
69 unsigned itemLen = (unsigned)strlen(item);
\r
73 for (j = 0; j < len; j++)
\r
75 unsigned c = item[j];
\r
76 if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j])
\r
86 static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
\r
88 ctrlType = ctrlType;
\r
93 static void PrintErrorMessage(const char *message)
\r
96 printf("\n7-Zip Error: %s\n", message);
\r
99 WCHAR messageW[256 + 4];
\r
101 for (i = 0; i < 256 && message[i] != 0; i++)
\r
102 messageW[i] = message[i];
\r
104 MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR);
\r
106 MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR);
\r
111 static WRes MyCreateDir(const WCHAR *name)
\r
113 return CreateDirectoryW(name, NULL) ? 0 : GetLastError();
\r
117 #define kBufferSize (1 << 13)
\r
119 #define kBufferSize (1 << 15)
\r
122 #define kSignatureSearchLimit (1 << 22)
\r
124 static Bool FindSignature(CSzFile *stream, UInt64 *resPos)
\r
126 Byte buf[kBufferSize];
\r
127 size_t numPrevBytes = 0;
\r
131 size_t numTests, pos;
\r
132 if (*resPos > kSignatureSearchLimit)
\r
137 size_t processed = kBufferSize - numPrevBytes;
\r
138 if (File_Read(stream, buf + numPrevBytes, &processed) != 0)
\r
140 if (processed == 0)
\r
142 numPrevBytes += processed;
\r
144 while (numPrevBytes <= k7zStartHeaderSize);
\r
146 numTests = numPrevBytes - k7zStartHeaderSize;
\r
147 for (pos = 0; pos < numTests; pos++)
\r
149 for (; buf[pos] != '7' && pos < numTests; pos++);
\r
150 if (pos == numTests)
\r
152 if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0)
\r
153 if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8))
\r
159 *resPos += numTests;
\r
160 numPrevBytes -= numTests;
\r
161 memmove(buf, buf + numTests, numPrevBytes);
\r
165 static Bool DoesFileOrDirExist(const WCHAR *path)
\r
167 WIN32_FIND_DATAW fd;
\r
169 handle = FindFirstFileW(path, &fd);
\r
170 if (handle == INVALID_HANDLE_VALUE)
\r
176 static WRes RemoveDirWithSubItems(WCHAR *path)
\r
178 WIN32_FIND_DATAW fd;
\r
181 size_t len = wcslen(path);
\r
182 wcscpy(path + len, L"*");
\r
183 handle = FindFirstFileW(path, &fd);
\r
185 if (handle == INVALID_HANDLE_VALUE)
\r
186 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, L"\\");
\r
196 res = RemoveDirWithSubItems(path);
\r
200 SetFileAttributesW(path, 0);
\r
201 if (DeleteFileW(path) == 0)
\r
202 res = GetLastError();
\r
207 if (!FindNextFileW(handle, &fd))
\r
209 res = GetLastError();
\r
210 if (res == ERROR_NO_MORE_FILES)
\r
219 if (!RemoveDirectoryW(path))
\r
220 res = GetLastError();
\r
226 int MY_CDECL main()
\r
228 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
234 lpCmdLine, int nCmdShow)
\r
237 CFileInStream archiveStream;
\r
238 CLookToRead lookStream;
\r
242 ISzAlloc allocTempImp;
\r
243 WCHAR sfxPath[MAX_PATH + 2];
\r
244 WCHAR path[MAX_PATH * 3 + 2];
\r
247 const wchar_t *cmdLineParams;
\r
248 const char *errorMessage = NULL;
\r
249 Bool useShellExecute = True;
\r
252 SetConsoleCtrlHandler(HandlerRoutine, TRUE);
\r
254 hInstance = hInstance;
\r
255 hPrevInstance = hPrevInstance;
\r
256 lpCmdLine = lpCmdLine;
\r
257 nCmdShow = nCmdShow;
\r
260 CrcGenerateTable();
\r
262 allocImp.Alloc = SzAlloc;
\r
263 allocImp.Free = SzFree;
\r
265 allocTempImp.Alloc = SzAllocTemp;
\r
266 allocTempImp.Free = SzFreeTemp;
\r
268 FileInStream_CreateVTable(&archiveStream);
\r
269 LookToRead_CreateVTable(&lookStream, False);
\r
271 winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
\r
272 if (winRes == 0 || winRes > MAX_PATH)
\r
275 cmdLineParams = GetCommandLineW();
\r
278 Bool quoteMode = False;
\r
279 for (;; cmdLineParams++)
\r
281 wchar_t c = *cmdLineParams;
\r
283 quoteMode = !quoteMode;
\r
284 else if (c == 0 || (c == L' ' && !quoteMode))
\r
294 winRes = GetTempPathW(MAX_PATH, path);
\r
295 if (winRes == 0 || winRes > MAX_PATH)
\r
297 pathLen = wcslen(path);
\r
298 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
\r
299 for (i = 0;; i++, d += GetTickCount())
\r
303 res = SZ_ERROR_FAIL;
\r
306 wcscpy(path + pathLen, L"7z");
\r
309 wchar_t *s = path + wcslen(path);
\r
312 for (k = 0; k < 8; k++)
\r
314 unsigned t = value & 0xF;
\r
316 s[7 - k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
\r
321 if (DoesFileOrDirExist(path))
\r
323 if (CreateDirectoryW(path, NULL))
\r
325 wcscat(path, L"\\");
\r
326 pathLen = wcslen(path);
\r
329 if (GetLastError() != ERROR_ALREADY_EXISTS)
\r
331 res = SZ_ERROR_FAIL;
\r
336 errorMessage = "Can't create temp folder";
\r
342 errorMessage = "Error";
\r
343 PrintErrorMessage(errorMessage);
\r
347 if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
\r
349 errorMessage = "can not open input file";
\r
350 res = SZ_ERROR_FAIL;
\r
355 if (!FindSignature(&archiveStream.file, &pos))
\r
356 res = SZ_ERROR_FAIL;
\r
357 else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)
\r
358 res = SZ_ERROR_FAIL;
\r
360 errorMessage = "Can't find 7z archive";
\r
365 lookStream.realStream = &archiveStream.s;
\r
366 LookToRead_Init(&lookStream);
\r
372 res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
\r
376 UInt32 executeFileIndex = (UInt32)(Int32)-1;
\r
377 UInt32 minPrice = 1 << 30;
\r
379 UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
\r
380 Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
\r
381 size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */
\r
383 for (i = 0; i < db.db.NumFiles; i++)
\r
386 size_t outSizeProcessed = 0;
\r
387 const CSzFileItem *f = db.db.Files + i;
\r
390 len = SzArEx_GetFileNameUtf16(&db, i, NULL);
\r
392 if (len >= MAX_PATH)
\r
394 res = SZ_ERROR_FAIL;
\r
398 temp = path + pathLen;
\r
400 SzArEx_GetFileNameUtf16(&db, i, temp);
\r
402 res = SzArEx_Extract(&db, &lookStream.s, i,
\r
403 &blockIndex, &outBuffer, &outBufferSize,
\r
404 &offset, &outSizeProcessed,
\r
405 &allocImp, &allocTempImp);
\r
411 size_t processedSize;
\r
413 size_t nameStartPos = 0;
\r
414 for (j = 0; temp[j] != 0; j++)
\r
416 if (temp[j] == '/')
\r
420 temp[j] = CHAR_PATH_SEPARATOR;
\r
421 nameStartPos = j + 1;
\r
433 const WCHAR *name = temp + nameStartPos;
\r
434 unsigned len = (unsigned)wcslen(name);
\r
435 unsigned nameLen = FindExt(temp + nameStartPos, &extLen);
\r
436 unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);
\r
437 unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);
\r
439 unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
\r
440 if (minPrice > price)
\r
443 executeFileIndex = i;
\r
444 useShellExecute = (extPrice != k_EXE_ExtIndex);
\r
447 if (DoesFileOrDirExist(path))
\r
449 errorMessage = "Duplicate file";
\r
450 res = SZ_ERROR_FAIL;
\r
453 if (OutFile_OpenW(&outFile, path))
\r
455 errorMessage = "Can't open output file";
\r
456 res = SZ_ERROR_FAIL;
\r
460 processedSize = outSizeProcessed;
\r
461 if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
\r
463 errorMessage = "Can't write output file";
\r
464 res = SZ_ERROR_FAIL;
\r
467 #ifdef USE_WINDOWS_FILE
\r
468 if (f->MTimeDefined)
\r
471 mTime.dwLowDateTime = f->MTime.Low;
\r
472 mTime.dwHighDateTime = f->MTime.High;
\r
473 SetFileTime(outFile.handle, NULL, NULL, &mTime);
\r
478 SRes res2 = File_Close(&outFile);
\r
487 #ifdef USE_WINDOWS_FILE
\r
488 if (f->AttribDefined)
\r
489 SetFileAttributesW(path, f->Attrib);
\r
496 if (executeFileIndex == (UInt32)(Int32)-1)
\r
498 errorMessage = "There is no file to execute";
\r
499 res = SZ_ERROR_FAIL;
\r
503 WCHAR *temp = path + pathLen;
\r
505 SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);
\r
506 for (j = 0; temp[j] != 0; j++)
\r
507 if (temp[j] == '/')
\r
508 temp[j] = CHAR_PATH_SEPARATOR;
\r
511 IAlloc_Free(&allocImp, outBuffer);
\r
513 SzArEx_Free(&db, &allocImp);
\r
515 File_Close(&archiveStream.file);
\r
519 HANDLE hProcess = 0;
\r
520 if (useShellExecute)
\r
522 SHELLEXECUTEINFO ei;
\r
526 memset(&ei, 0, sizeof(ei));
\r
527 ei.cbSize = sizeof(ei);
\r
529 ei.fMask = SEE_MASK_NOCLOSEPROCESS
\r
531 | SEE_MASK_FLAG_DDEWAIT
\r
533 /* | SEE_MASK_NO_CONSOLE */
\r
535 if (wcslen(cmdLineParams) != 0)
\r
536 ei.lpParameters = cmdLineParams;
\r
537 ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */
\r
538 success = ShellExecuteEx(&ei);
\r
539 executeRes = (UINT32)(UINT_PTR)ei.hInstApp;
\r
540 if (!success || (executeRes <= 32 && executeRes != 0)) /* executeRes = 0 in Windows CE */
\r
541 res = SZ_ERROR_FAIL;
\r
543 hProcess = ei.hProcess;
\r
548 PROCESS_INFORMATION pi;
\r
549 WCHAR cmdLine[MAX_PATH * 3];
\r
551 wcscpy(cmdLine, path);
\r
552 wcscat(cmdLine, cmdLineParams);
\r
553 memset(&si, 0, sizeof(si));
\r
554 si.cb = sizeof(si);
\r
555 if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
\r
556 res = SZ_ERROR_FAIL;
\r
559 CloseHandle(pi.hThread);
\r
560 hProcess = pi.hProcess;
\r
565 WaitForSingleObject(hProcess, INFINITE);
\r
566 CloseHandle(hProcess);
\r
570 path[pathLen] = L'\0';
\r
571 RemoveDirWithSubItems(path);
\r
577 if (res == SZ_ERROR_UNSUPPORTED)
\r
578 errorMessage = "Decoder doesn't support this archive";
\r
579 else if (res == SZ_ERROR_MEM)
\r
580 errorMessage = "Can't allocate required memory";
\r
581 else if (res == SZ_ERROR_CRC)
\r
582 errorMessage = "CRC error";
\r
586 errorMessage = "ERROR";
\r
589 PrintErrorMessage(errorMessage);
\r