all repos — mgba @ dab12cf5c674542cae0db7708c333035255fbc65

mGBA Game Boy Advance Emulator

src/third-party/lzma/Util/SfxSetup/SfxSetup.c (view raw)

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