all repos — mgba @ b2d406a411b31acce5bbf0246af32a80c22ca834

mGBA Game Boy Advance Emulator

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

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