mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-23 22:56:50 +00:00
618 lines
14 KiB
C
618 lines
14 KiB
C
/* SfxSetup.c - 7z SFX Setup
|
|
2014-12-07 : Igor Pavlov : Public domain */
|
|
|
|
#include "Precomp.h"
|
|
|
|
#ifndef UNICODE
|
|
#define UNICODE
|
|
#endif
|
|
|
|
#ifndef _UNICODE
|
|
#define _UNICODE
|
|
#endif
|
|
|
|
#ifdef _CONSOLE
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#include "../../7z.h"
|
|
#include "../../7zAlloc.h"
|
|
#include "../../7zCrc.h"
|
|
#include "../../7zFile.h"
|
|
#include "../../CpuArch.h"
|
|
|
|
#define k_EXE_ExtIndex 2
|
|
|
|
static const char *kExts[] =
|
|
{
|
|
"bat"
|
|
, "cmd"
|
|
, "exe"
|
|
, "inf"
|
|
, "msi"
|
|
#ifdef UNDER_CE
|
|
, "cab"
|
|
#endif
|
|
, "html"
|
|
, "htm"
|
|
};
|
|
|
|
static const char *kNames[] =
|
|
{
|
|
"setup"
|
|
, "install"
|
|
, "run"
|
|
, "start"
|
|
};
|
|
|
|
static unsigned FindExt(const wchar_t *s, unsigned *extLen)
|
|
{
|
|
unsigned len = (unsigned)wcslen(s);
|
|
unsigned i;
|
|
for (i = len; i > 0; i--)
|
|
{
|
|
if (s[i - 1] == '.')
|
|
{
|
|
*extLen = len - i;
|
|
return i - 1;
|
|
}
|
|
}
|
|
*extLen = 0;
|
|
return len;
|
|
}
|
|
|
|
#define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) -= 0x20 : (c)))
|
|
|
|
static unsigned FindItem(const char **items, unsigned num, const wchar_t *s, unsigned len)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
const char *item = items[i];
|
|
unsigned itemLen = (unsigned)strlen(item);
|
|
unsigned j;
|
|
if (len != itemLen)
|
|
continue;
|
|
for (j = 0; j < len; j++)
|
|
{
|
|
unsigned c = item[j];
|
|
if (c != s[j] && MAKE_CHAR_UPPER(c) != s[j])
|
|
break;
|
|
}
|
|
if (j == len)
|
|
return i;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
#ifdef _CONSOLE
|
|
static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
|
|
{
|
|
ctrlType = ctrlType;
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
static void PrintErrorMessage(const char *message)
|
|
{
|
|
#ifdef _CONSOLE
|
|
printf("\n7-Zip Error: %s\n", message);
|
|
#else
|
|
#ifdef UNDER_CE
|
|
WCHAR messageW[256 + 4];
|
|
unsigned i;
|
|
for (i = 0; i < 256 && message[i] != 0; i++)
|
|
messageW[i] = message[i];
|
|
messageW[i] = 0;
|
|
MessageBoxW(0, messageW, L"7-Zip Error", MB_ICONERROR);
|
|
#else
|
|
MessageBoxA(0, message, "7-Zip Error", MB_ICONERROR);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static WRes MyCreateDir(const WCHAR *name)
|
|
{
|
|
return CreateDirectoryW(name, NULL) ? 0 : GetLastError();
|
|
}
|
|
|
|
#ifdef UNDER_CE
|
|
#define kBufferSize (1 << 13)
|
|
#else
|
|
#define kBufferSize (1 << 15)
|
|
#endif
|
|
|
|
#define kSignatureSearchLimit (1 << 22)
|
|
|
|
static Bool FindSignature(CSzFile *stream, UInt64 *resPos)
|
|
{
|
|
Byte buf[kBufferSize];
|
|
size_t numPrevBytes = 0;
|
|
*resPos = 0;
|
|
for (;;)
|
|
{
|
|
size_t processed, pos;
|
|
if (*resPos > kSignatureSearchLimit)
|
|
return False;
|
|
processed = kBufferSize - numPrevBytes;
|
|
if (File_Read(stream, buf + numPrevBytes, &processed) != 0)
|
|
return False;
|
|
processed += numPrevBytes;
|
|
if (processed < k7zStartHeaderSize ||
|
|
(processed == k7zStartHeaderSize && numPrevBytes != 0))
|
|
return False;
|
|
processed -= k7zStartHeaderSize;
|
|
for (pos = 0; pos <= processed; pos++)
|
|
{
|
|
for (; buf[pos] != '7' && pos <= processed; pos++);
|
|
if (pos > processed)
|
|
break;
|
|
if (memcmp(buf + pos, k7zSignature, k7zSignatureSize) == 0)
|
|
if (CrcCalc(buf + pos + 12, 20) == GetUi32(buf + pos + 8))
|
|
{
|
|
*resPos += pos;
|
|
return True;
|
|
}
|
|
}
|
|
*resPos += processed;
|
|
numPrevBytes = k7zStartHeaderSize;
|
|
memmove(buf, buf + processed, k7zStartHeaderSize);
|
|
}
|
|
}
|
|
|
|
static Bool DoesFileOrDirExist(const WCHAR *path)
|
|
{
|
|
WIN32_FIND_DATAW fd;
|
|
HANDLE handle;
|
|
handle = FindFirstFileW(path, &fd);
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
return False;
|
|
FindClose(handle);
|
|
return True;
|
|
}
|
|
|
|
static WRes RemoveDirWithSubItems(WCHAR *path)
|
|
{
|
|
WIN32_FIND_DATAW fd;
|
|
HANDLE handle;
|
|
WRes res = 0;
|
|
size_t len = wcslen(path);
|
|
wcscpy(path + len, L"*");
|
|
handle = FindFirstFileW(path, &fd);
|
|
path[len] = L'\0';
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
return GetLastError();
|
|
for (;;)
|
|
{
|
|
if (wcscmp(fd.cFileName, L".") != 0 &&
|
|
wcscmp(fd.cFileName, L"..") != 0)
|
|
{
|
|
wcscpy(path + len, fd.cFileName);
|
|
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
|
{
|
|
wcscat(path, WSTRING_PATH_SEPARATOR);
|
|
res = RemoveDirWithSubItems(path);
|
|
}
|
|
else
|
|
{
|
|
SetFileAttributesW(path, 0);
|
|
if (DeleteFileW(path) == 0)
|
|
res = GetLastError();
|
|
}
|
|
if (res != 0)
|
|
break;
|
|
}
|
|
if (!FindNextFileW(handle, &fd))
|
|
{
|
|
res = GetLastError();
|
|
if (res == ERROR_NO_MORE_FILES)
|
|
res = 0;
|
|
break;
|
|
}
|
|
}
|
|
path[len] = L'\0';
|
|
FindClose(handle);
|
|
if (res == 0)
|
|
{
|
|
if (!RemoveDirectoryW(path))
|
|
res = GetLastError();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef _CONSOLE
|
|
int MY_CDECL main()
|
|
#else
|
|
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|
#ifdef UNDER_CE
|
|
LPWSTR
|
|
#else
|
|
LPSTR
|
|
#endif
|
|
lpCmdLine, int nCmdShow)
|
|
#endif
|
|
{
|
|
CFileInStream archiveStream;
|
|
CLookToRead lookStream;
|
|
CSzArEx db;
|
|
SRes res = SZ_OK;
|
|
ISzAlloc allocImp;
|
|
ISzAlloc allocTempImp;
|
|
WCHAR sfxPath[MAX_PATH + 2];
|
|
WCHAR path[MAX_PATH * 3 + 2];
|
|
#ifndef UNDER_CE
|
|
WCHAR workCurDir[MAX_PATH + 32];
|
|
#endif
|
|
size_t pathLen;
|
|
DWORD winRes;
|
|
const wchar_t *cmdLineParams;
|
|
const char *errorMessage = NULL;
|
|
Bool useShellExecute = True;
|
|
|
|
#ifdef _CONSOLE
|
|
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
|
|
#else
|
|
hInstance = hInstance;
|
|
hPrevInstance = hPrevInstance;
|
|
lpCmdLine = lpCmdLine;
|
|
nCmdShow = nCmdShow;
|
|
#endif
|
|
|
|
CrcGenerateTable();
|
|
|
|
allocImp.Alloc = SzAlloc;
|
|
allocImp.Free = SzFree;
|
|
|
|
allocTempImp.Alloc = SzAllocTemp;
|
|
allocTempImp.Free = SzFreeTemp;
|
|
|
|
FileInStream_CreateVTable(&archiveStream);
|
|
LookToRead_CreateVTable(&lookStream, False);
|
|
|
|
winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
|
|
if (winRes == 0 || winRes > MAX_PATH)
|
|
return 1;
|
|
{
|
|
cmdLineParams = GetCommandLineW();
|
|
#ifndef UNDER_CE
|
|
{
|
|
Bool quoteMode = False;
|
|
for (;; cmdLineParams++)
|
|
{
|
|
wchar_t c = *cmdLineParams;
|
|
if (c == L'\"')
|
|
quoteMode = !quoteMode;
|
|
else if (c == 0 || (c == L' ' && !quoteMode))
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
{
|
|
unsigned i;
|
|
DWORD d;
|
|
winRes = GetTempPathW(MAX_PATH, path);
|
|
if (winRes == 0 || winRes > MAX_PATH)
|
|
return 1;
|
|
pathLen = wcslen(path);
|
|
d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
|
|
|
|
for (i = 0;; i++, d += GetTickCount())
|
|
{
|
|
if (i >= 100)
|
|
{
|
|
res = SZ_ERROR_FAIL;
|
|
break;
|
|
}
|
|
wcscpy(path + pathLen, L"7z");
|
|
|
|
{
|
|
wchar_t *s = path + wcslen(path);
|
|
UInt32 value = d;
|
|
unsigned k;
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
unsigned t = value & 0xF;
|
|
value >>= 4;
|
|
s[7 - k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
|
|
}
|
|
s[k] = '\0';
|
|
}
|
|
|
|
if (DoesFileOrDirExist(path))
|
|
continue;
|
|
if (CreateDirectoryW(path, NULL))
|
|
{
|
|
wcscat(path, WSTRING_PATH_SEPARATOR);
|
|
pathLen = wcslen(path);
|
|
break;
|
|
}
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
|
{
|
|
res = SZ_ERROR_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef UNDER_CE
|
|
wcscpy(workCurDir, path);
|
|
#endif
|
|
if (res != SZ_OK)
|
|
errorMessage = "Can't create temp folder";
|
|
}
|
|
|
|
if (res != SZ_OK)
|
|
{
|
|
if (!errorMessage)
|
|
errorMessage = "Error";
|
|
PrintErrorMessage(errorMessage);
|
|
return 1;
|
|
}
|
|
|
|
if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
|
|
{
|
|
errorMessage = "can not open input file";
|
|
res = SZ_ERROR_FAIL;
|
|
}
|
|
else
|
|
{
|
|
UInt64 pos = 0;
|
|
if (!FindSignature(&archiveStream.file, &pos))
|
|
res = SZ_ERROR_FAIL;
|
|
else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)
|
|
res = SZ_ERROR_FAIL;
|
|
if (res != 0)
|
|
errorMessage = "Can't find 7z archive";
|
|
}
|
|
|
|
if (res == SZ_OK)
|
|
{
|
|
lookStream.realStream = &archiveStream.s;
|
|
LookToRead_Init(&lookStream);
|
|
}
|
|
|
|
SzArEx_Init(&db);
|
|
if (res == SZ_OK)
|
|
{
|
|
res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
|
|
}
|
|
|
|
if (res == SZ_OK)
|
|
{
|
|
UInt32 executeFileIndex = (UInt32)(Int32)-1;
|
|
UInt32 minPrice = 1 << 30;
|
|
UInt32 i;
|
|
UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
|
|
Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
|
|
size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */
|
|
|
|
for (i = 0; i < db.NumFiles; i++)
|
|
{
|
|
size_t offset = 0;
|
|
size_t outSizeProcessed = 0;
|
|
size_t len;
|
|
WCHAR *temp;
|
|
len = SzArEx_GetFileNameUtf16(&db, i, NULL);
|
|
|
|
if (len >= MAX_PATH)
|
|
{
|
|
res = SZ_ERROR_FAIL;
|
|
break;
|
|
}
|
|
|
|
temp = path + pathLen;
|
|
|
|
SzArEx_GetFileNameUtf16(&db, i, temp);
|
|
{
|
|
res = SzArEx_Extract(&db, &lookStream.s, i,
|
|
&blockIndex, &outBuffer, &outBufferSize,
|
|
&offset, &outSizeProcessed,
|
|
&allocImp, &allocTempImp);
|
|
if (res != SZ_OK)
|
|
break;
|
|
}
|
|
{
|
|
CSzFile outFile;
|
|
size_t processedSize;
|
|
size_t j;
|
|
size_t nameStartPos = 0;
|
|
for (j = 0; temp[j] != 0; j++)
|
|
{
|
|
if (temp[j] == '/')
|
|
{
|
|
temp[j] = 0;
|
|
MyCreateDir(path);
|
|
temp[j] = CHAR_PATH_SEPARATOR;
|
|
nameStartPos = j + 1;
|
|
}
|
|
}
|
|
|
|
if (SzArEx_IsDir(&db, i))
|
|
{
|
|
MyCreateDir(path);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
unsigned extLen;
|
|
const WCHAR *name = temp + nameStartPos;
|
|
unsigned len = (unsigned)wcslen(name);
|
|
unsigned nameLen = FindExt(temp + nameStartPos, &extLen);
|
|
unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);
|
|
unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);
|
|
|
|
unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
|
|
if (minPrice > price)
|
|
{
|
|
minPrice = price;
|
|
executeFileIndex = i;
|
|
useShellExecute = (extPrice != k_EXE_ExtIndex);
|
|
}
|
|
|
|
if (DoesFileOrDirExist(path))
|
|
{
|
|
errorMessage = "Duplicate file";
|
|
res = SZ_ERROR_FAIL;
|
|
break;
|
|
}
|
|
if (OutFile_OpenW(&outFile, path))
|
|
{
|
|
errorMessage = "Can't open output file";
|
|
res = SZ_ERROR_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
processedSize = outSizeProcessed;
|
|
if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
|
|
{
|
|
errorMessage = "Can't write output file";
|
|
res = SZ_ERROR_FAIL;
|
|
}
|
|
|
|
#ifdef USE_WINDOWS_FILE
|
|
if (SzBitWithVals_Check(&db.MTime, i))
|
|
{
|
|
const CNtfsFileTime *t = db.MTime.Vals + i;
|
|
FILETIME mTime;
|
|
mTime.dwLowDateTime = t->Low;
|
|
mTime.dwHighDateTime = t->High;
|
|
SetFileTime(outFile.handle, NULL, NULL, &mTime);
|
|
}
|
|
#endif
|
|
|
|
{
|
|
SRes res2 = File_Close(&outFile);
|
|
if (res != SZ_OK)
|
|
break;
|
|
if (res2 != SZ_OK)
|
|
{
|
|
res = res2;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef USE_WINDOWS_FILE
|
|
if (SzBitWithVals_Check(&db.Attribs, i))
|
|
SetFileAttributesW(path, db.Attribs.Vals[i]);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (res == SZ_OK)
|
|
{
|
|
if (executeFileIndex == (UInt32)(Int32)-1)
|
|
{
|
|
errorMessage = "There is no file to execute";
|
|
res = SZ_ERROR_FAIL;
|
|
}
|
|
else
|
|
{
|
|
WCHAR *temp = path + pathLen;
|
|
UInt32 j;
|
|
SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);
|
|
for (j = 0; temp[j] != 0; j++)
|
|
if (temp[j] == '/')
|
|
temp[j] = CHAR_PATH_SEPARATOR;
|
|
}
|
|
}
|
|
IAlloc_Free(&allocImp, outBuffer);
|
|
}
|
|
SzArEx_Free(&db, &allocImp);
|
|
|
|
File_Close(&archiveStream.file);
|
|
|
|
if (res == SZ_OK)
|
|
{
|
|
HANDLE hProcess = 0;
|
|
|
|
#ifndef UNDER_CE
|
|
WCHAR oldCurDir[MAX_PATH + 2];
|
|
oldCurDir[0] = 0;
|
|
{
|
|
DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);
|
|
if (needLen == 0 || needLen > MAX_PATH)
|
|
oldCurDir[0] = 0;
|
|
SetCurrentDirectory(workCurDir);
|
|
}
|
|
#endif
|
|
|
|
if (useShellExecute)
|
|
{
|
|
SHELLEXECUTEINFO ei;
|
|
UINT32 executeRes;
|
|
BOOL success;
|
|
|
|
memset(&ei, 0, sizeof(ei));
|
|
ei.cbSize = sizeof(ei);
|
|
ei.lpFile = path;
|
|
ei.fMask = SEE_MASK_NOCLOSEPROCESS
|
|
#ifndef UNDER_CE
|
|
| SEE_MASK_FLAG_DDEWAIT
|
|
#endif
|
|
/* | SEE_MASK_NO_CONSOLE */
|
|
;
|
|
if (wcslen(cmdLineParams) != 0)
|
|
ei.lpParameters = cmdLineParams;
|
|
ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */
|
|
success = ShellExecuteEx(&ei);
|
|
executeRes = (UINT32)(UINT_PTR)ei.hInstApp;
|
|
if (!success || (executeRes <= 32 && executeRes != 0)) /* executeRes = 0 in Windows CE */
|
|
res = SZ_ERROR_FAIL;
|
|
else
|
|
hProcess = ei.hProcess;
|
|
}
|
|
else
|
|
{
|
|
STARTUPINFOW si;
|
|
PROCESS_INFORMATION pi;
|
|
WCHAR cmdLine[MAX_PATH * 3];
|
|
|
|
wcscpy(cmdLine, path);
|
|
wcscat(cmdLine, cmdLineParams);
|
|
memset(&si, 0, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
|
|
res = SZ_ERROR_FAIL;
|
|
else
|
|
{
|
|
CloseHandle(pi.hThread);
|
|
hProcess = pi.hProcess;
|
|
}
|
|
}
|
|
|
|
if (hProcess != 0)
|
|
{
|
|
WaitForSingleObject(hProcess, INFINITE);
|
|
CloseHandle(hProcess);
|
|
}
|
|
|
|
#ifndef UNDER_CE
|
|
SetCurrentDirectory(oldCurDir);
|
|
#endif
|
|
}
|
|
|
|
path[pathLen] = L'\0';
|
|
RemoveDirWithSubItems(path);
|
|
|
|
if (res == SZ_OK)
|
|
return 0;
|
|
|
|
{
|
|
if (res == SZ_ERROR_UNSUPPORTED)
|
|
errorMessage = "Decoder doesn't support this archive";
|
|
else if (res == SZ_ERROR_MEM)
|
|
errorMessage = "Can't allocate required memory";
|
|
else if (res == SZ_ERROR_CRC)
|
|
errorMessage = "CRC error";
|
|
else
|
|
{
|
|
if (!errorMessage)
|
|
errorMessage = "ERROR";
|
|
}
|
|
if (errorMessage)
|
|
PrintErrorMessage(errorMessage);
|
|
}
|
|
return 1;
|
|
}
|