all repos — mgba @ 22245617f434049f4646916d1b2930d376503b0d

mGBA Game Boy Advance Emulator

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

  1/* SfxSetup.c - 7z SFX Setup
  22014-12-07 : 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 *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 *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 **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 = 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  ctrlType = 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 (; buf[pos] != '7' && pos <= processed; 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  for (;;)
186  {
187    if (wcscmp(fd.cFileName, L".") != 0 &&
188        wcscmp(fd.cFileName, L"..") != 0)
189    {
190      wcscpy(path + len, fd.cFileName);
191      if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
192      {
193        wcscat(path, WSTRING_PATH_SEPARATOR);
194        res = RemoveDirWithSubItems(path);
195      }
196      else
197      {
198        SetFileAttributesW(path, 0);
199        if (DeleteFileW(path) == 0)
200          res = GetLastError();
201      }
202      if (res != 0)
203        break;
204    }
205    if (!FindNextFileW(handle, &fd))
206    {
207      res = GetLastError();
208      if (res == ERROR_NO_MORE_FILES)
209        res = 0;
210      break;
211    }
212  }
213  path[len] = L'\0';
214  FindClose(handle);
215  if (res == 0)
216  {
217    if (!RemoveDirectoryW(path))
218      res = GetLastError();
219  }
220  return res;
221}
222
223#ifdef _CONSOLE
224int MY_CDECL main()
225#else
226int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
227  #ifdef UNDER_CE
228  LPWSTR
229  #else
230  LPSTR
231  #endif
232  lpCmdLine, int nCmdShow)
233#endif
234{
235  CFileInStream archiveStream;
236  CLookToRead lookStream;
237  CSzArEx db;
238  SRes res = SZ_OK;
239  ISzAlloc allocImp;
240  ISzAlloc allocTempImp;
241  WCHAR sfxPath[MAX_PATH + 2];
242  WCHAR path[MAX_PATH * 3 + 2];
243  #ifndef UNDER_CE
244  WCHAR workCurDir[MAX_PATH + 32];
245  #endif
246  size_t pathLen;
247  DWORD winRes;
248  const wchar_t *cmdLineParams;
249  const char *errorMessage = NULL;
250  Bool useShellExecute = True;
251
252  #ifdef _CONSOLE
253  SetConsoleCtrlHandler(HandlerRoutine, TRUE);
254  #else
255  hInstance = hInstance;
256  hPrevInstance = hPrevInstance;
257  lpCmdLine = lpCmdLine;
258  nCmdShow = nCmdShow;
259  #endif
260
261  CrcGenerateTable();
262
263  allocImp.Alloc = SzAlloc;
264  allocImp.Free = SzFree;
265
266  allocTempImp.Alloc = SzAllocTemp;
267  allocTempImp.Free = SzFreeTemp;
268
269  FileInStream_CreateVTable(&archiveStream);
270  LookToRead_CreateVTable(&lookStream, False);
271 
272  winRes = GetModuleFileNameW(NULL, sfxPath, MAX_PATH);
273  if (winRes == 0 || winRes > MAX_PATH)
274    return 1;
275  {
276    cmdLineParams = GetCommandLineW();
277    #ifndef UNDER_CE
278    {
279      Bool quoteMode = False;
280      for (;; cmdLineParams++)
281      {
282        wchar_t c = *cmdLineParams;
283        if (c == L'\"')
284          quoteMode = !quoteMode;
285        else if (c == 0 || (c == L' ' && !quoteMode))
286          break;
287      }
288    }
289    #endif
290  }
291
292  {
293    unsigned i;
294    DWORD d;
295    winRes = GetTempPathW(MAX_PATH, path);
296    if (winRes == 0 || winRes > MAX_PATH)
297      return 1;
298    pathLen = wcslen(path);
299    d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
300    
301    for (i = 0;; i++, d += GetTickCount())
302    {
303      if (i >= 100)
304      {
305        res = SZ_ERROR_FAIL;
306        break;
307      }
308      wcscpy(path + pathLen, L"7z");
309
310      {
311        wchar_t *s = path + wcslen(path);
312        UInt32 value = d;
313        unsigned k;
314        for (k = 0; k < 8; k++)
315        {
316          unsigned t = value & 0xF;
317          value >>= 4;
318          s[7 - k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
319        }
320        s[k] = '\0';
321      }
322
323      if (DoesFileOrDirExist(path))
324        continue;
325      if (CreateDirectoryW(path, NULL))
326      {
327        wcscat(path, WSTRING_PATH_SEPARATOR);
328        pathLen = wcslen(path);
329        break;
330      }
331      if (GetLastError() != ERROR_ALREADY_EXISTS)
332      {
333        res = SZ_ERROR_FAIL;
334        break;
335      }
336    }
337    
338    #ifndef UNDER_CE
339    wcscpy(workCurDir, path);
340    #endif
341    if (res != SZ_OK)
342      errorMessage = "Can't create temp folder";
343  }
344
345  if (res != SZ_OK)
346  {
347    if (!errorMessage)
348      errorMessage = "Error";
349    PrintErrorMessage(errorMessage);
350    return 1;
351  }
352
353  if (InFile_OpenW(&archiveStream.file, sfxPath) != 0)
354  {
355    errorMessage = "can not open input file";
356    res = SZ_ERROR_FAIL;
357  }
358  else
359  {
360    UInt64 pos = 0;
361    if (!FindSignature(&archiveStream.file, &pos))
362      res = SZ_ERROR_FAIL;
363    else if (File_Seek(&archiveStream.file, (Int64 *)&pos, SZ_SEEK_SET) != 0)
364      res = SZ_ERROR_FAIL;
365    if (res != 0)
366      errorMessage = "Can't find 7z archive";
367  }
368
369  if (res == SZ_OK)
370  {
371    lookStream.realStream = &archiveStream.s;
372    LookToRead_Init(&lookStream);
373  }
374
375  SzArEx_Init(&db);
376  if (res == SZ_OK)
377  {
378    res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
379  }
380  
381  if (res == SZ_OK)
382  {
383    UInt32 executeFileIndex = (UInt32)(Int32)-1;
384    UInt32 minPrice = 1 << 30;
385    UInt32 i;
386    UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
387    Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
388    size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */
389    
390    for (i = 0; i < db.NumFiles; i++)
391    {
392      size_t offset = 0;
393      size_t outSizeProcessed = 0;
394      size_t len;
395      WCHAR *temp;
396      len = SzArEx_GetFileNameUtf16(&db, i, NULL);
397      
398      if (len >= MAX_PATH)
399      {
400        res = SZ_ERROR_FAIL;
401        break;
402      }
403      
404      temp = path + pathLen;
405      
406      SzArEx_GetFileNameUtf16(&db, i, temp);
407      {
408        res = SzArEx_Extract(&db, &lookStream.s, i,
409          &blockIndex, &outBuffer, &outBufferSize,
410          &offset, &outSizeProcessed,
411          &allocImp, &allocTempImp);
412        if (res != SZ_OK)
413          break;
414      }
415      {
416        CSzFile outFile;
417        size_t processedSize;
418        size_t j;
419        size_t nameStartPos = 0;
420        for (j = 0; temp[j] != 0; j++)
421        {
422          if (temp[j] == '/')
423          {
424            temp[j] = 0;
425            MyCreateDir(path);
426            temp[j] = CHAR_PATH_SEPARATOR;
427            nameStartPos = j + 1;
428          }
429        }
430
431        if (SzArEx_IsDir(&db, i))
432        {
433          MyCreateDir(path);
434          continue;
435        }
436        else
437        {
438          unsigned extLen;
439          const WCHAR *name = temp + nameStartPos;
440          unsigned len = (unsigned)wcslen(name);
441          unsigned nameLen = FindExt(temp + nameStartPos, &extLen);
442          unsigned extPrice = FindItem(kExts, sizeof(kExts) / sizeof(kExts[0]), name + len - extLen, extLen);
443          unsigned namePrice = FindItem(kNames, sizeof(kNames) / sizeof(kNames[0]), name, nameLen);
444
445          unsigned price = namePrice + extPrice * 64 + (nameStartPos == 0 ? 0 : (1 << 12));
446          if (minPrice > price)
447          {
448            minPrice = price;
449            executeFileIndex = i;
450            useShellExecute = (extPrice != k_EXE_ExtIndex);
451          }
452         
453          if (DoesFileOrDirExist(path))
454          {
455            errorMessage = "Duplicate file";
456            res = SZ_ERROR_FAIL;
457            break;
458          }
459          if (OutFile_OpenW(&outFile, path))
460          {
461            errorMessage = "Can't open output file";
462            res = SZ_ERROR_FAIL;
463            break;
464          }
465        }
466  
467        processedSize = outSizeProcessed;
468        if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
469        {
470          errorMessage = "Can't write output file";
471          res = SZ_ERROR_FAIL;
472        }
473        
474        #ifdef USE_WINDOWS_FILE
475        if (SzBitWithVals_Check(&db.MTime, i))
476        {
477          const CNtfsFileTime *t = db.MTime.Vals + i;
478          FILETIME mTime;
479          mTime.dwLowDateTime = t->Low;
480          mTime.dwHighDateTime = t->High;
481          SetFileTime(outFile.handle, NULL, NULL, &mTime);
482        }
483        #endif
484        
485        {
486          SRes res2 = File_Close(&outFile);
487          if (res != SZ_OK)
488            break;
489          if (res2 != SZ_OK)
490          {
491            res = res2;
492            break;
493          }
494        }
495        #ifdef USE_WINDOWS_FILE
496        if (SzBitWithVals_Check(&db.Attribs, i))
497          SetFileAttributesW(path, db.Attribs.Vals[i]);
498        #endif
499      }
500    }
501
502    if (res == SZ_OK)
503    {
504      if (executeFileIndex == (UInt32)(Int32)-1)
505      {
506        errorMessage = "There is no file to execute";
507        res = SZ_ERROR_FAIL;
508      }
509      else
510      {
511        WCHAR *temp = path + pathLen;
512        UInt32 j;
513        SzArEx_GetFileNameUtf16(&db, executeFileIndex, temp);
514        for (j = 0; temp[j] != 0; j++)
515          if (temp[j] == '/')
516            temp[j] = CHAR_PATH_SEPARATOR;
517      }
518    }
519    IAlloc_Free(&allocImp, outBuffer);
520  }
521  SzArEx_Free(&db, &allocImp);
522
523  File_Close(&archiveStream.file);
524
525  if (res == SZ_OK)
526  {
527    HANDLE hProcess = 0;
528    
529    #ifndef UNDER_CE
530    WCHAR oldCurDir[MAX_PATH + 2];
531    oldCurDir[0] = 0;
532    {
533      DWORD needLen = GetCurrentDirectory(MAX_PATH + 1, oldCurDir);
534      if (needLen == 0 || needLen > MAX_PATH)
535        oldCurDir[0] = 0;
536      SetCurrentDirectory(workCurDir);
537    }
538    #endif
539    
540    if (useShellExecute)
541    {
542      SHELLEXECUTEINFO ei;
543      UINT32 executeRes;
544      BOOL success;
545      
546      memset(&ei, 0, sizeof(ei));
547      ei.cbSize = sizeof(ei);
548      ei.lpFile = path;
549      ei.fMask = SEE_MASK_NOCLOSEPROCESS
550          #ifndef UNDER_CE
551          | SEE_MASK_FLAG_DDEWAIT
552          #endif
553          /* | SEE_MASK_NO_CONSOLE */
554          ;
555      if (wcslen(cmdLineParams) != 0)
556        ei.lpParameters = cmdLineParams;
557      ei.nShow = SW_SHOWNORMAL; /* SW_HIDE; */
558      success = ShellExecuteEx(&ei);
559      executeRes = (UINT32)(UINT_PTR)ei.hInstApp;
560      if (!success || (executeRes <= 32 && executeRes != 0))  /* executeRes = 0 in Windows CE */
561        res = SZ_ERROR_FAIL;
562      else
563        hProcess = ei.hProcess;
564    }
565    else
566    {
567      STARTUPINFOW si;
568      PROCESS_INFORMATION pi;
569      WCHAR cmdLine[MAX_PATH * 3];
570
571      wcscpy(cmdLine, path);
572      wcscat(cmdLine, cmdLineParams);
573      memset(&si, 0, sizeof(si));
574      si.cb = sizeof(si);
575      if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
576        res = SZ_ERROR_FAIL;
577      else
578      {
579        CloseHandle(pi.hThread);
580        hProcess = pi.hProcess;
581      }
582    }
583    
584    if (hProcess != 0)
585    {
586      WaitForSingleObject(hProcess, INFINITE);
587      CloseHandle(hProcess);
588    }
589    
590    #ifndef UNDER_CE
591    SetCurrentDirectory(oldCurDir);
592    #endif
593  }
594
595  path[pathLen] = L'\0';
596  RemoveDirWithSubItems(path);
597
598  if (res == SZ_OK)
599    return 0;
600  
601  {
602    if (res == SZ_ERROR_UNSUPPORTED)
603      errorMessage = "Decoder doesn't support this archive";
604    else if (res == SZ_ERROR_MEM)
605      errorMessage = "Can't allocate required memory";
606    else if (res == SZ_ERROR_CRC)
607      errorMessage = "CRC error";
608    else
609    {
610      if (!errorMessage)
611        errorMessage = "ERROR";
612    }
613    if (errorMessage)
614      PrintErrorMessage(errorMessage);
615  }
616  return 1;
617}