/*---------------------------------------------------------------------------*/
/* File:        main.cpp                                                     */
/* Created:     Fri, 29 Jul 2005 03:23:00 GMT                                */
/*              by Oleg N. Scherbakov, mailto:oleg@7zsfx.info                */
/* Last update: Wed, 13 Oct 2010 09:53:16 GMT                                */
/*              by Oleg N. Scherbakov, mailto:oleg@7zsfx.info                */
/* Revision:    1903                                                         */
/*---------------------------------------------------------------------------*/
/* Revision:    1794                                                         */
/* Updated:     Sat, 26 Jun 2010 10:45:08 GMT                                */
/*              by Oleg N. Scherbakov, mailto:oleg@7zsfx.info                */
/* Description: Add copyrights to -sfxversion                                */
/*---------------------------------------------------------------------------*/
/* Revision:    1774                                                         */
/* Updated:     Sun, 06 Jun 2010 08:51:48 GMT                                */
/*              by Oleg N. Scherbakov, mailto:oleg@7zsfx.info                */
/* Description: Add warning stuff											 */
/*				(WarningTitle, MiscFlags, -mf command line switch)			 */
/*---------------------------------------------------------------------------*/
/* Revision:    1698                                                         */
/* Updated:     Mon, 22 Mar 2010 11:26:58 GMT                                */
/*              by Oleg N. Scherbakov, mailto:oleg@7zsfx.info                */
/* Description: New file stamp.                                              */
/*---------------------------------------------------------------------------*/
#include "stdafx.h"
#include "7zSfxModInt.h"
#include "SfxDialogs.h"
#include "ExtractEngine.h"
#include "Helpers.h"
#include "Splash.h"

#pragma warning( disable:4005 )

#define SFX_USE_DLL			0x01
#define METHOD_LZMA			0x02
#define METHOD_DEFLATE		0x04
#define METHOD_PPMD			0x08
#define METHOD_BCJ			0x10
#define METHOD_BCJ2			0x20
#define METHOD_LZMA2		0x40

#ifndef _DLL
    #undef	SFX_USE_DLL
    #define SFX_USE_DLL		0x00
#endif // _DLL

#ifndef COMPRESS_LZMA
    #undef	METHOD_LZMA
    #define	METHOD_LZMA		0x00
#endif // LZMA

#ifndef COMPRESS_DEFLATE
    #undef	METHOD_DEFLATE
    #define	METHOD_DEFLATE	0x00
#endif // Deflate

#ifndef COMPRESS_PPMD
    #undef	METHOD_PPMD
    #define	METHOD_PPMD		0x00
#endif // PPMd

#ifndef COMPRESS_BCJ
    #undef	METHOD_BCJ
    #define	METHOD_BCJ		0x00
#endif // BCJ

#ifndef COMPRESS_BCJ2
    #undef	METHOD_BCJ2
    #define	METHOD_BCJ2		0x00
#endif // BCJ2

#ifndef COMPRESS_LZMA2
    #undef	METHOD_LZMA2
    #define	METHOD_LZMA2	0x00
#endif // LZMA2

#pragma warning( default:4005 )

#define SKIP_WHITESPACES_W(str) 	while( *str != L'\0' && ((unsigned)*str) <= L' ' ) str++;

HRESULT ExtractArchive( IInStream * inStream, const UString &folderName );
bool	ReadConfig( IInStream * inStream, LPCSTR startID, LPCSTR endID, AString &stringResult );
void	DeleteSFX( UString moduleName );

UString MultiByteToUnicodeString( const AString &srcString, UINT codePage );
AString UnicodeStringToMultiByte( const UString &srcString, UINT codePage );

// Larger image need header section to be moved
const UInt64 kMaxCheckStartPosition = 1 << 25;

static char kSignatureConfigStart[] = ",!@Install@!UTF-8!";
static char kSignatureConfigEnd[] = ",!@InstallEnd@!";

LPCWSTR	lpwszErrorTitle;
#ifdef _SFX_USE_WARNINGS
    LPCWSTR lpwszWarningTitle;
    UString	strWarningTitle;
#endif // _SFX_USE_WARNINGS
LPCWSTR	lpwszTitle;
LPCWSTR lpwszExtractTitle;
LPCWSTR lpwszExtractPathTitle;
LPCWSTR lpwszExtractPathText;
LPCWSTR lpwszCancelPrompt;
LPCWSTR	lpwszCancelText;
LPCWSTR	lpwszExtractDialogText;

int		GUIMode = 0;
int		GUIFlags = -1;
int		MiscFlags = 0;
int		ExtractDialogWidth = 300;
int		ExtractPathWidth = 300;
int		OverwriteMode = OVERWRITE_MODE_ALL;
int		OverwriteFlags = 0;
int		FinishMessage = -1;

UString	extractPath;
UString	defaultExtractPath;
UString newFootprint;	//if this extraction creates a new trail, this var will hold the root of it
UString strSfxFolder;
UString strSfxName;
UString	strErrorTitle;
UString	strTitle;

UString	strModulePathName;
bool	fUseInstallPath;
bool	deleteExtraction = false;	//set by the '-d, --isDelete' arg of the PFW commandLine

CObjectVector<CTextConfigPair> Variables;
bool fUseBackward = false;

typedef struct tagENVALIAS {
    int		nFolder;
    LPCWSTR	lpwszName;
} ENVALIAS, * PENVALIAS;

#ifndef CSIDL_COMMON_DOCUMENTS
    #define CSIDL_COMMON_DOCUMENTS          0x002e        // All Users\Documents
#endif

ENVALIAS EnvAliases [] = {
    { CSIDL_COMMON_DESKTOPDIRECTORY,	L"CommonDesktop" },
    { CSIDL_COMMON_DOCUMENTS,			L"CommonDocuments" },
    { CSIDL_DESKTOPDIRECTORY,			L"UserDesktop" },
    { CSIDL_PERSONAL,					L"MyDocuments" },
    { CSIDL_PERSONAL,					L"MyDocs" }
};

SplashCore* g_pSplashCore = NULL;


void SfxInit()
{
#ifdef _SFX_USE_LANG
    GetUILanguage();
#endif // _SFX_USE_LANG

    lpwszErrorTitle = GetLanguageString( STR_ERROR_TITLE );
#ifdef _SFX_USE_WARNINGS
    lpwszWarningTitle = GetLanguageString( STR_WARNING_TITLE );
#endif // _SFX_USE_WARNINGS
    lpwszTitle = GetLanguageString( STR_TITLE );
    lpwszExtractTitle = GetLanguageString( STR_EXTRACT_TITLE );
    lpwszExtractPathTitle = GetLanguageString( STR_EXTRACT_PATH_TITLE );
    lpwszExtractPathText = GetLanguageString( STR_EXTRACT_PATH_TEXT );
    lpwszCancelPrompt = GetLanguageString( STR_CANCEL_PROMPT );
    lpwszCancelText = lpwszExtractDialogText = NULL;

    WCHAR	wszPath[MAX_PATH+1];
    WCHAR	wszName[64];
    for( int i = 0; i < 0x40; i++ )
    {
        if( SHGetSpecialFolderPath(NULL,wszPath,i,FALSE) != FALSE )
        {
            wsprintf( wszName, SPECIAL_FOLDER_FORMAT, i );
            CTextConfigPair var;
            var.ID = wszName;
            var.String = wszPath;
            Variables.Add( var );
            for( int j = 0; j < (sizeof(EnvAliases)/sizeof(EnvAliases[0])); j++ )
            {
                if( EnvAliases[j].nFolder == i )
                {
                    var.ID = EnvAliases[j].lpwszName;
                    var.String = wszPath;
                    Variables.Add( var );
                }
            }
        }
    }
    InitCommonControls();
}

bool CreateSecondStage(const UString& firstStagePFW, UString& secondStagePFW)
{
    secondStagePFW.Empty();
    if(PathFileExists(static_cast<LPCWSTR>(firstStagePFW)) == FALSE)
    {
        LOG(Error, "Unable to determine stage-1 PFW '" + static_cast<LPCWSTR>(firstStagePFW) + "': " + GetLastError());
        return false;
    }

    LOG(Info, "Setting up for 2nd stage PFW execution");
    int fileNameSeparator = firstStagePFW.ReverseFind_PathSepar();
    if(fileNameSeparator == -1)
    {
        LOG(Error, "Unable to determine file name of stage-1 PFW: " + static_cast<LPCWSTR>(firstStagePFW));
        return false;
    }

    UString pfwFileName = firstStagePFW.Mid(fileNameSeparator+1, firstStagePFW.Len()-fileNameSeparator);

    DWORD dwLastError = 0;
    UString secondStageLocation = CreateTempName(L"7ZipSfx.%03x");
    //if(CreateSecureFolder(static_cast<LPCWSTR>(secondStageLocation), dwLastError))
    if(::CreateDirectory(static_cast<LPCWSTR>(secondStageLocation), NULL) != FALSE)
    {
        secondStageLocation.Add_PathSepar();
        secondStagePFW = secondStageLocation + pfwFileName;
        LOG(Info, "Creating stage-2 PFW: " + static_cast<LPCWSTR>(secondStagePFW));
        if(CopyFile(static_cast<LPCWSTR>(firstStagePFW), static_cast<LPCWSTR>(secondStagePFW), FALSE) == FALSE)
        {
            LOG(Error, "Creation of stage-2 PFW failed: " + GetLastError());
            return false;
        }
        MoveFileEx(static_cast<LPCWSTR>(secondStagePFW), NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
        MoveFileEx(static_cast<LPCWSTR>(secondStageLocation), NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
        return true;  //success!
    }
    else
    {
        LOG(Error, "Failed to initialize 2nd stage location: " + static_cast<LPCWSTR>(secondStageLocation));
    }

    return false;
}
/*
UString CreateTempName( LPCWSTR lpwszFormat )
{
    LOG(Info, "Creating temp name");
    UString path;
    DWORD dwSize = ::GetTempPath( 1, path.GetBuf(2) );
    path.ReleaseBuf_SetEnd(0);
    if( dwSize > 0 )
    {
        LOG(Info, "Getting temp path");
        ::GetTempPath( dwSize+1, path.GetBuf(dwSize+1) );
        LOG(Info, "Got" + (LPCWSTR)path);
        path.ReleaseBuf_CalcLen(dwSize+1);
        path.ReleaseBuf_SetEnd(path.Len());
    }

    for( int i = 0; i < 0xfff; i++ )
    {
        wchar_t tempExtractFolder[15];
        wsprintf(static_cast<LPWSTR>(tempExtractFolder), lpwszFormat, i);
        if( ::GetFileAttributes( path + tempExtractFolder ) == (DWORD)-1 )
        {
            path += tempExtractFolder;
            break;
        }
    }
    return path;
}
*/
void SetExtractionPathToTemp (UString& extractionPath)
{
    extractionPath = CreateTempName(L"7ZipSfx.%03x");
    LOG(Info, "Extraction temp path is" + static_cast<LPCWSTR>(extractionPath));

    //clear out the temp folder if (in an unlikely event) it already exists and is not empty
    for(int count=0; count<5; count++)
    {
        DWORD ret=0;
        if(!DirectoryExists(extractionPath, ret) || PathIsDirectoryEmpty(extractionPath))
            return;

        LOG(Info, "Extraction temp path not empty. Nuking it out..");
        if(DeleteFileOrDirectoryAlways(extractionPath))
            return;

        LOG(Info, "Extraction temp path was not cleaned up. Trying again..");
        count++;
    }
    LOG(Info, "Unable to create a good temp folder for extraction.");
}

void ReplaceVariables(UString& str)
{
    ExpandEnvironmentStrings(str);
    ReplaceWithArchivePath(str, strSfxFolder);
    ReplaceWithArchiveName(str, strSfxName);
    ExpandEnvironmentStrings(str);
}

void ReplaceVariablesEx(UString& str)
{
    ExpandEnvironmentStrings(str);
    ReplaceWithExtractPath(str, extractPath);
    ReplaceVariables(str);
}

void SetEnvironment()
{
    LOG(Info, "Setting environment variables");
    for(int i = 0; i < Variables.Size(); i++)
    {
        UString val = Variables[i].String;
        ReplaceVariablesEx(val);
        LOG(Info, "Setting environment variable" + Variables[i].ID + "as" + (LPCWSTR)val);
        ::SetEnvironmentVariable( Variables[i].ID, val );
    }
    LOG(Info, "Done setting environment variables");
}

LPCWSTR IsSubString( LPCWSTR lpwszString, LPCWSTR lpwszSubString )
{
    int nLength = lstrlenW( lpwszSubString );
    if( lstrlenW( lpwszString ) >= nLength &&
            _wcsnicmp( lpwszString, lpwszSubString, nLength ) == 0 )
        return lpwszString + nLength;
    return NULL;
}

#define	CPF_NONE		0
#define CPF_DIGIT		1
#define	CPF_NUMBER		2
#define CPF_SFX_INDEX	3

LPCWSTR CheckPrefix( LPCWSTR lpwszString, LPCWSTR lpwszPrefix, BOOL nCPFlags )
{
    LPCWSTR p = IsSubString( lpwszString, lpwszPrefix );
    if( p == NULL )
        return NULL;
    switch( nCPFlags )
    {
    case CPF_DIGIT:
        if( *p >= L'0' && *p <= L'9' && p[1] == L':' )
            return p+2;
        break;
    case CPF_NUMBER:
        if( *p >= L'0' && *p <= L'9' )
        {
            while( *p >= L'0' && *p <= L'9' ) p++;
            if( *p == L':' )
                return p+1;
        }
        break;
    case CPF_SFX_INDEX:
        if( p[1] == ':' )
        {
            if( (*p >= L'0' && *p <= L'9') ||
                (*p >= L'a' && *p <= L'z') ||
                (*p >= L'A' && *p <= L'Z') )
            {
                return p+2;
            }
        }
        break;
    default:
        if( *p == L':' )
            return p+1;
    }
    return NULL;
}


#define IsCommandLineSwitch( lpwszCommandLine, lpwszSwitch )	\
    (IsSubString( lpwszCommandLine+1, lpwszSwitch ) != NULL )


LPCWSTR UpdateFlagsCommon( LPCWSTR lpwszText, int * pnValue )
{
    while( (*lpwszText >= L'0' && *lpwszText <= L'9') || *lpwszText == L'+' || *lpwszText == L'-' )
    {
        int nValue = StringToLong( lpwszText );
        // skip to next
        while( *lpwszText == L'-' || *lpwszText == L'+' ) lpwszText++;
        while( *lpwszText >= L'0' && *lpwszText <= L'9' ) lpwszText++;
        if( nValue > 0 )
            pnValue[0] |= nValue;
        else
            pnValue[0] &= (~(0-nValue));
    }

    return lpwszText;
}

LPCWSTR UpdateGUIFlags( LPCWSTR lpwszText )
{
    if( GUIFlags == -1 || (*lpwszText != L'+' && *lpwszText != L'-') )
        GUIFlags = 0;

    return UpdateFlagsCommon( lpwszText, &GUIFlags );
}

LPCWSTR UpdateMiscFlags( LPCWSTR lpwszText )
{
    if( *lpwszText != L'+' && *lpwszText != L'-' )
        MiscFlags = 0;

    return UpdateFlagsCommon( lpwszText, &MiscFlags );
}

LPCWSTR UpdateOverwriteMode( LPCWSTR lpwszText )
{
    int nValue = OverwriteMode | OverwriteFlags;
    if( *lpwszText != L'+' && *lpwszText != L'-' )
        nValue = OVERWRITE_MODE_ALL;
    LPCWSTR lpwszRet = UpdateFlagsCommon( lpwszText, &nValue );
    if( (nValue&OVERWRITE_MODE_MASK) <= OVERWRITE_MODE_MAX )
        OverwriteMode = nValue & OVERWRITE_MODE_MASK;
    OverwriteFlags = nValue & (~OVERWRITE_MODE_MASK);
    return lpwszRet;
}

void GetConfigDirectory( CObjectVector<CTextConfigPair>& config, UString & result )
{
    LPCWSTR lpwszValue;
    result.Empty();
    if( (lpwszValue = GetTextConfigValue( config, CFG_DIRECTORY)) != NULL )
    {
        result = lpwszValue;
        ReplaceVariablesEx( result );
        if( result[result.Len()-1] != L'\\' && result[result.Len()-1] != L'/' )
            result += L'\\';
    }
}

LPCWSTR LoadQuotedString( LPCWSTR lpwszSrc, UString & result )
{
    if( *lpwszSrc == L'\"' )
    {
        lpwszSrc++;
        while( *lpwszSrc != L'\0' && *lpwszSrc != L'\"' )
        {
            result += *lpwszSrc;
            lpwszSrc++;
        }
        if( *lpwszSrc != L'\0' )
            lpwszSrc++;
    }
    else
    {
        while( *lpwszSrc != L'\0' && ((unsigned)*lpwszSrc) > L' ' )
        {
            result += *lpwszSrc;
            lpwszSrc++;
        }
    }

    SKIP_WHITESPACES_W( lpwszSrc );
    return lpwszSrc;
}

bool SaveConfiguration( LPCWSTR lpwszFileName, AString & CfgData )
{
    COutFileStream	cfg;
    UInt32 writed;
    static BYTE utf8prefix[4] = { 0xEF, 0xBB, 0xBF, 0 };
    if( cfg.Create( lpwszFileName, true ) == false )
    {
        UString filePath = lpwszFileName;
        int nPos  = GetDirectorySeparatorPos( filePath );
        if( nPos < 0 )
            return false;
        filePath.ReleaseBuf_SetEnd( nPos );
        if( CreateFolderTree( (LPCWSTR)filePath ) == FALSE || cfg.Create( lpwszFileName, true ) == false )
            return false;
    }
    AString fullCfg;
    fullCfg = (char *)utf8prefix;
    fullCfg += kSignatureConfigStart;
    fullCfg += (LPCSTR)CfgData;
    fullCfg += kSignatureConfigEnd;
    if( cfg.Write(fullCfg, fullCfg.Len(), &writed) != S_OK || writed != (UInt32)fullCfg.Len() )
        return false;
    return true;
}

void ReplaceVariableInShortcut( UString& strShortcut, UString& strVarName, UString& strVarValue )
{
    int nVarNameLength = strVarName.Len();
    for( int i = 0; i < strShortcut.Len(); i++ )
    {
        if( strShortcut[i] == L'%' &&
                MyStrincmp( static_cast<LPCWSTR>(strShortcut)+i+1, strVarName, nVarNameLength ) == 0 &&
                    strShortcut[i+nVarNameLength+1] == L'%' )
        {
            // var found
            strShortcut.Delete( i, nVarNameLength+2 );
            strShortcut.Insert( i, strVarValue );
        }
    }
}

LPCWSTR IsSfxSwitch( LPCWSTR lpwszCommandLine, LPCWSTR lpwszSwitch )
{
    // 1. check old notation
    if( lpwszCommandLine[0] == L'-' && lpwszCommandLine[1] == '-' )
    {
        lpwszCommandLine++;
    }
    // 2. New (common) notation
    if( lpwszCommandLine[0] == L'-' || lpwszCommandLine[0] == '/')
    {
        int nSwitchLength = lstrlen(lpwszSwitch);
        if( MyStrincmp( lpwszCommandLine+1, lpwszSwitch, lstrlen(lpwszSwitch) ) == 0 &&
                (((unsigned)lpwszCommandLine[nSwitchLength+1]) <= L' ' || lpwszCommandLine[nSwitchLength+1] == L':') )
        {
            return lpwszCommandLine+nSwitchLength+1;
        }
    }

    return NULL;
}

void CreateLanguageSignature( DWORD dwLangId, AString& strBegin, AString& strEnd )
{
    strBegin = kSignatureConfigStart;
    strEnd = kSignatureConfigEnd;
    strBegin = strBegin.Left( strBegin.Len()-1 );
    strEnd = strEnd.Left( strEnd.Len()-1 );

    CHAR Buf[100];
    wsprintfA( Buf, ":Language:%u!", dwLangId );
    strBegin += Buf;
    strEnd += Buf;
}

LPCWSTR ParseConfigOverride( LPCWSTR lpwszCommandLine, CObjectVector<CTextConfigPair> & pairs )
{
    static LPCWSTR ConfigParams[] = {
        CFG_TITLE,
        CFG_ERRORTITLE,
#ifdef _SFX_USE_WARNINGS
        CFG_WARNINGTITLE,
#endif // _SFX_USE_WARNINGS
        CFG_GUIMODE,
        CFG_GUIFLAGS,
        CFG_MISCFLAGS,
        CFG_BEGINPROMPT,
        CFG_INSTALLPATH,
        CFG_EXTRACT_TITLE,
        CFG_EXTRACT_CANCELTEXT,
        CFG_EXTRACT_DIALOGTEXT,
        CFG_EXTRACT_DIALOGWIDTH,
        CFG_DELETE,
        CFG_SELFDELETE,
        CFG_EXTRACT_PATH_TITLE,
        CFG_EXTRACT_PATH_TEXT,
        CFG_HELP_TEXT,
        CFG_OVERWRITE_MODE,
        CFG_CANCEL_PROMPT,
        CFG_EXTRACTPATH_WIDTH,
        CFG_FINISHMESSAGE,
        CFG_EXECUTEFILE,
        CFG_EXECUTEPARAMETERS,
        CFG_DIRECTORY,
        CFG_PROGRESS,
        CFG_SETENVIRONMENT,
        NULL
    };

    LPCWSTR * pParams = ConfigParams;
    while( *pParams != NULL )
    {
        int nLen = lstrlen(*pParams);
        if( MyStrincmp( lpwszCommandLine, *pParams, nLen ) == 0 && lpwszCommandLine[nLen] == L'=' )
        {
            UString str = lpwszCommandLine;
            LPCWSTR p = str;
            bool inQuotes=false;
            while( *p )
            {
                if( *p <= L' ' && inQuotes == false )
                    break;
                if( *p == L'\"' )
                {
                    if( inQuotes == false )
                        inQuotes = true;
                    else
                        inQuotes = false;
                }
                else
                {
                    if( p[0] == L'\\' && p[1] == L'\"' )
                        p++;
                }
                p++;
            }
            int nSwitchLen = (int)(p-(LPCWSTR)str);
            str.ReleaseBuf_SetEnd( nSwitchLen );
            AString	aStr= UnicodeStringToMultiByte( str, CP_UTF8 );
            if( GetTextConfig( aStr, pairs, true ) == false )
                return (LPCWSTR)1;
            return lpwszCommandLine + nSwitchLen;
        }
        pParams++;
    }
    return NULL;
}

void SetConfigVariables( CObjectVector<CTextConfigPair>& pairs )
{
    LOG(Info, "Setting config variables");
    LPCWSTR	lpwszValue;
    int from;

    // Main title
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_TITLE )) != NULL )
    {
        strErrorTitle = lpwszValue; strErrorTitle += GetLanguageString( STR_ERROR_SUFFIX );
        lpwszErrorTitle = strErrorTitle;
#ifdef _SFX_USE_WARNINGS
        strWarningTitle = lpwszValue; strWarningTitle += GetLanguageString( STR_WARNING_SUFFIX );
        lpwszWarningTitle = strWarningTitle;
#endif // _SFX_USE_WARNINGS
        lpwszTitle = lpwszValue;
    }
    // Update error title
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_ERRORTITLE )) != NULL )
        lpwszErrorTitle = lpwszValue;
#ifdef _SFX_USE_WARNINGS
    // Update warnings title
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_WARNINGTITLE )) != NULL )
        lpwszWarningTitle = lpwszValue;
#endif // _SFX_USE_WARNINGS
    // Extract title
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_EXTRACT_TITLE )) != NULL )
        lpwszExtractTitle = lpwszValue;
    // Load GUIMode & 7-Zip > 4.42 'Progress'
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_PROGRESS)) != NULL && lstrcmpi( lpwszValue, L"no" ) == 0 )
        GUIMode = GUIMODE_HIDDEN;
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_GUIMODE )) != NULL )
    {
        if( lpwszValue[0] >= L'0' && lpwszValue[0] <= L'0'+GUIMODE_MAX )
        {
            GUIMode = lpwszValue[0] - L'0';
        }

        g_pSplashCore->init(GUIMode);
    }
    // Load OverwriteMode
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_OVERWRITE_MODE)) != NULL )
    {
        UpdateOverwriteMode( lpwszValue );
    }
    // Load GUIFlags
    from = 0;
    while( (lpwszValue = GetTextConfigValue( pairs, CFG_GUIFLAGS, &from )) != NULL )
    {
        from++;
        UpdateGUIFlags( lpwszValue );
    }
    // Load MiscFlags
    from = 0;
    while( (lpwszValue = GetTextConfigValue( pairs, CFG_MISCFLAGS, &from )) != NULL )
    {
        from++;
        UpdateMiscFlags( lpwszValue );
    }

    lpwszCancelText = GetTextConfigValue( pairs, CFG_EXTRACT_CANCELTEXT );
    lpwszExtractDialogText = GetTextConfigValue( pairs, CFG_EXTRACT_DIALOGTEXT );
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_EXTRACT_DIALOGWIDTH)) != NULL )
        ExtractDialogWidth = StringToLong( lpwszValue );
    if( (lpwszValue = GetTextConfigValue( pairs, CFG_EXTRACTPATH_WIDTH)) != NULL )
        ExtractPathWidth = StringToLong( lpwszValue );

    // Extract path strings
    if( (lpwszValue = GetTextConfigValue(pairs,CFG_EXTRACT_PATH_TITLE)) != NULL )
        lpwszExtractPathTitle = lpwszValue;
    if( (lpwszValue = GetTextConfigValue(pairs,CFG_EXTRACT_PATH_TEXT)) != NULL )
        lpwszExtractPathText = lpwszValue;

    // Cancel prompt text
    if( (lpwszValue = GetTextConfigValue(pairs,CFG_CANCEL_PROMPT)) != NULL )
        lpwszCancelPrompt = lpwszValue;

}

void OverrideConfigParam( LPCWSTR lpwszName, LPCWSTR lpwszValue, CObjectVector<CTextConfigPair>& pairs )
{
    UString strParam = lpwszName;
    strParam += L"=\"";
    while( *lpwszValue > L' ' ) strParam += *lpwszValue++;
    strParam += L"\"";
    ParseConfigOverride( strParam, pairs );
}

void PostExecute_Shortcut( LPCWSTR lpwszValue )
{
    UString strShortcut = lpwszValue;
    ReplaceWithExtractPath( strShortcut, extractPath );
    ReplaceWithArchivePath( strShortcut, strSfxFolder );
    ReplaceWithArchiveName( strShortcut, strSfxName );
    for( int i = 0; i < Variables.Size(); i++ )
    {
        UString varValue = MyGetEnvironmentVariable( Variables[i].ID );
        ExpandEnvironmentStrings( varValue );
        ReplaceWithExtractPath( varValue, extractPath );
        ReplaceWithArchivePath( varValue, strSfxFolder );
        ReplaceWithArchiveName( varValue, strSfxName );

        ReplaceVariableInShortcut( strShortcut, Variables[i].ID, varValue );
    }
    CreateShortcut( strShortcut );
}

void PostExecute_Delete(LPCWSTR lpwszValue)
{
    UString tmp = lpwszValue;
    ReplaceVariablesEx(tmp);
    LOG(Info, "Post install cleanup, deleting: " + static_cast<LPCWSTR>(tmp));
    DeleteFileOrDirectoryAlways(tmp);
}

typedef void (* POST_EXECUTE_PROC)( LPCWSTR lpwszParamName );

void ProcessPostExecuteSub( POST_EXECUTE_PROC pfnPostExecute,
                            CObjectVector<CTextConfigPair>& pairs,
                            LPCWSTR lpwszParamPrefix,
                            LPCWSTR lpwszBatchIndexes,
                            int nUseDefault )
{
        UString	ustrParamName;
        LPCWSTR	lpwszValue;
        LPCWSTR p1 = lpwszBatchIndexes;
        bool	fBatchExecuted = false;
        int nIndex;
        while( (*p1 >= L'0' && *p1 <= L'9') ||
               (*p1 >= L'a' && *p1 <= L'z') ||
               (*p1 >= L'A' && *p1 <= L'Z') ||
               (lpwszBatchIndexes == p1 && nUseDefault == -1) )
        {
            ustrParamName = lpwszParamPrefix;
            ustrParamName += *p1;
            nIndex = 0;
            p1++;

            while( (lpwszValue = GetTextConfigValue( pairs, ustrParamName, &nIndex)) != NULL )
            {
                pfnPostExecute( lpwszValue );
                nIndex++;
                fBatchExecuted = true;
            }
        }
        switch( nUseDefault )
        {
        case 0:
            break;
        case 1:
            ProcessPostExecuteSub( pfnPostExecute, pairs, lpwszParamPrefix, L"\0", -1 );
            break;
        default:
            if( *lpwszBatchIndexes == L'\0' && nUseDefault == -1 )
                break;
            if( fBatchExecuted == false && *lpwszBatchIndexes != L'\0' )
                ProcessPostExecuteSub( pfnPostExecute, pairs, lpwszParamPrefix, L"\0",  -1 );
        }
}


bool VerifyExtractionPath(UString& extractionPath, DWORD& error)
{
    error=0;
    fUseInstallPath = false;

    //Extraction path specified?
    if(extractionPath.IsEmpty())
    {
        LOG(Warning, "Extraction path not specified. Directing extraction to a temporary folder");
        SetExtractionPathToTemp(extractionPath);
        return true;
        //Should we fail this as an error instead now?
    }

    //Process the specified extraction path
    UString tmp = extractionPath;
    ReplaceVariables(tmp);
    if(tmp.IsEmpty())
    {
        LOG(Info, "Specified extraction path resolves to NUL. Directing extraction to a temporary folder");
        SetExtractionPathToTemp(extractionPath);
        return true;
    }

    fUseInstallPath = true;
    LOG(Info, "Verifying the extraction path:" + static_cast<LPCWSTR>(extractionPath));
    if(extractionPath[extractionPath.Len()-1] == L'\\' || extractionPath[extractionPath.Len()-1] == L'/')
    {
        extractionPath.ReleaseBuf_SetEnd(extractionPath.Len()-1); //discard trailing slash, if any
    }

    DWORD ret = 0;
    //Extraction path a root directory?
    if(extractionPath == GetParentDirectory(extractionPath))
    {
        LOG(Info, "Extraction path set to the root directory");
        if(GUIMode == GUIMODE_HIDDEN)
        {
            LOG(Error, "Cannot extract to a root directory. Exiting..");
            error = ERR_CREATE_FOLDER;
        }
        else
        {
            if(DirectoryExists(extractionPath, ret))
            {
                LOG(Error, "Cannot secure ACLs on a root directory");
            }
            else
            {
                LOG(Error, "Extraction path set to a non-existent drive!!");
            }
            LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
            lpwszErrorTitle = L"Invalid Path";
            SfxErrorDialog(FALSE, ERR_CREATE_FOLDER, static_cast<LPCWSTR>(extractPath));
            lpwszErrorTitle = lpwszErrorTitleSave;
        }
        return false;
    }
    //Extraction path usable as it is?
    if(!DirectoryExists(extractionPath, ret))
    {
        LOG(Info, "Extraction path does not physically exist");
        return true;
    }
    if(PathIsDirectoryEmpty(extractionPath))
    {
        LOG(Info, "Extraction path exists but is empty, deleting it" + ret);
        if(DeleteFileOrDirectoryAlways(extractionPath))
        {
            return true;
        }
        else
        {
            ret = ::GetLastError();
            LOG(Warning, "Deletion of empty extraction path failed with error: " + ret);
            if(deleteExtraction)
            {
                LOG(Info, "Extracted package would be deleted upon exit, redirecting extraction to a temp folder");
                SetExtractionPathToTemp(extractionPath);
                return true;
            }

            LOG(Error, "Cannot continue extraction. Exiting..");
            if(GUIMode != GUIMODE_HIDDEN)
                error = ret;
            else
                error = ERRC_EXTRACT_DELETE_OLD;
            return false;
        }
    }

    //Extraction path _not_ usable in its current form... recover or die!
    LOG(Info, "Extraction path is not empty");
    if(GUIMode == GUIMODE_HIDDEN)
    {
        LOG(Info, "Silent Extraction (no GUI)");
        if(OverwriteMode == OVERWRITE_MODE_ALL)
        {
            if(DeleteFileOrDirectoryAlways(extractionPath))
            {
                LOG(Info, "Extraction path nuked of old crud, continuing..");
                return true;
            }
            else
            {
                ret = ::GetLastError();
                LOG(Warning, "Unable to empty out the extraction path, error: " + ret);
                if(deleteExtraction)
                {
                    LOG(Info, "Redirecting extraction to a temp folder since the package would be deleted upon exit");
                    SetExtractionPathToTemp(extractionPath);
                    return true;
                }
                LOG(Error, "Cannot continue extraction. Exiting..")
                error = ERRC_EXTRACT_DELETE_OLD;
                return false;
            }
        }
        else
        {
            LOG(Info, "Overwrite disabled, specified extraction path cannot be cleaned up for extraction. Exiting..");
            error = ERRC_EXTRACT_OVERWRITE_DISABLED;
            return false;
        }
    }
    else
    {
        LOG(Info, "Extraction in GUI mode");
        if(extractionPath == defaultExtractPath)
        {
            if(OverwriteMode == OVERWRITE_MODE_ALL)
            {
                if(DeleteFileOrDirectoryAlways(extractionPath))
                {
                    LOG(Info, "Extraction path nuked of old crud, continuing..");
                    return true;
                }
                else
                {
                    LOG(Error, "Unable to empty out the extraction path. Redirecting extraction to a temp folder");
                    SetExtractionPathToTemp(extractionPath);
                    return true;
                }
            }
            else
            {
                LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
                lpwszErrorTitle = L"Overinstall prohibited";
                SfxErrorDialog(FALSE, ERR_DELETE_FILE, static_cast<LPCWSTR>(extractPath));
                lpwszErrorTitle = lpwszErrorTitleSave;
                return false;
            }
        }
        else
        {
            LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
            lpwszErrorTitle = L"Folder exists";
            SfxErrorDialog(FALSE, ERR_EXTRACT, static_cast<LPCWSTR>(extractPath));
            lpwszErrorTitle = lpwszErrorTitleSave;
            return false;
        }
    }

    return true;
}

bool GetExtractionFootprint(const UString& extractPath, DWORD& ret)
{
    ret = 0;
    UString parent;
    UString currentFolder = extractPath;
    //go up one folder at a time till we hit a folder that already exists
    while(!DirectoryExists(currentFolder, ret))
    {
        LOG(Info, "Dir does not exist" + static_cast<LPCWSTR>(currentFolder));

        parent = GetParentDirectory(currentFolder);
        //if the input path cannot be traversed up any further, 'GetParentDirectory' returns the input path
        if(parent == currentFolder)
        {
            LOG(Error, "Extraction path on a non-existent drive" + static_cast<LPCWSTR>(extractPath));
            if(GUIMode != GUIMODE_HIDDEN)
            {
                LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
                lpwszErrorTitle = L"Invalid Path";
                SfxErrorDialog(FALSE, ERR_CREATE_FOLDER, static_cast<LPCWSTR>(extractPath));
                lpwszErrorTitle = lpwszErrorTitleSave;
            }
            return false;
        }

        newFootprint = currentFolder;
        currentFolder = parent;
    }

    LOG(Info, "New footprint created by this instance is rooted at" + static_cast<LPCWSTR>(newFootprint));
    return true;
}

void ShowSfxVersion()
{
    GUIFlags = GUIFLAGS_XPSTYLE;
    CSfxDialog_Version dlg;
    extern unsigned int g_NumCodecs;
    extern const CCodecInfo *g_Codecs[];
    UString ustrVersion = GetLanguageString( STR_SFXVERSION );
    for( unsigned int ki = 0; ki < g_NumCodecs; ki++ )
    {
        if( ki != 0 )
            ustrVersion += L", ";
        ustrVersion += (const wchar_t *)(g_Codecs[ki]->Name);
    }
    ustrVersion += L"\n\n\n";
    ustrVersion += GetLanguageString( STR_COPYRIGHT );
    dlg.Show( SD_OK|SD_ICONINFORMATION, lpwszTitle, ustrVersion );
}

bool CreateExtractionMutex(HANDLE& extractMutexHandle, DWORD& ret)
{
    ret = 0;
    if(extractPath.IsEmpty())
    {
        LOG(Info, "Extraction path still empty. Trying once more to direct extraction to a temporary folder");
        SetExtractionPathToTemp(extractPath);
        if(extractPath.IsEmpty())
        {
            LOG(Info, "Still no extraction path available. Aborting..");
            return false;
        }
    }

    UString extractPathMutex = extractPath;
    extractPathMutex.Replace(L"\\", L"*");	//backslash not allowed on mutex name, replace it with the asterix character
    extractPathMutex.Replace(L"/", L"*");		//so that only one mutex exists on any path
    extractPathMutex.MakeLower_Ascii();
    extractPathMutex = static_cast<UString>(L"Global\\") + extractPathMutex;
    if(extractPathMutex.Len() >= MAX_PATH)
    {
        extractPathMutex.ReleaseBuf_SetEnd(MAX_PATH -1);
    }

    LOG(Info, "Creating named mutex:" + static_cast<LPCWSTR>(extractPathMutex));
    extractMutexHandle = ::CreateMutex(NULL, FALSE, static_cast<LPCTSTR>(extractPathMutex));

    ret = GetLastError();
    if(extractMutexHandle == NULL)
    {
        LOG(Info, "Mutex creation failed with error:" + ret);
        return false;
    }

    if(ret == 0)
    {
        LOG(Info, "Mutex creation successful");
        return true;
    }

    LOG(Info, "Non-zero error code return by CreateMutex:" + ret);
    if(ret == ERROR_ALREADY_EXISTS)
    {
        ret = ERR_MULTIPLE_INSTALL;
        if(GUIMode != GUIMODE_HIDDEN)
        {
            LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
            lpwszErrorTitle = GetLanguageString(STR_GENERIC_ERROR);
            SfxErrorDialog( FALSE, ERR_MULTIPLE_INSTALL);
            lpwszErrorTitle = lpwszErrorTitleSave;
        }
    }
    return false;
}

bool AcquireExtractionMutex(HANDLE& extractMutexHandle, DWORD& ret)
{
    ret = 0;
    switch(::WaitForSingleObject(extractMutexHandle, 0))
    {
        case WAIT_TIMEOUT:
            if(GUIMode != GUIMODE_HIDDEN)
            {
                LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
                lpwszErrorTitle = GetLanguageString(STR_GENERIC_ERROR);
                SfxErrorDialog(FALSE, ERR_MULTIPLE_INSTALL);
                lpwszErrorTitle = lpwszErrorTitleSave;
            }
            LOG(Error, "Mutex wait timed out, another PFW instance running");
            ret = ERR_MULTIPLE_INSTALL;
            return false;

        case WAIT_FAILED:
            LOG(Warning, "Mutex wait failed with error:" + GetLastError());
        case WAIT_ABANDONED:
            LOG(Warning, "Mutex wait abandoned, continuing nonetheless");
        case WAIT_OBJECT_0:
            LOG(Info, "Acquired mutex");
            return true;
    }
    ret = ERR_7Z_INTERNAL_ERROR;
    return false;
}

void MutexRelease(HANDLE& extractMutexHandle)
{
    LOG(Info, "Releasing Mutex");
    if(extractMutexHandle != NULL)
    {
        if(ReleaseMutex(extractMutexHandle) == FALSE)
        {
            LOG(Warning, "Mutex Release failed with error:" + GetLastError());
        }
    }
    else
    {
        LOG(Warning, "Mutex already released or was abandoned");
    }

    if(CloseHandle(extractMutexHandle) == FALSE)
    {
        LOG(Warning, "Closing mutex handle failed with error:" + GetLastError());
    }

    extractMutexHandle = NULL;
    return;
}

void SfxCleanup()
{
    LOG(Info, "Performing cleanup operations" + static_cast<LPCWSTR>(extractPath));

    if(extractPath == GetParentDirectory(extractPath))
    {
        LOG(Info, "Skipping cleanup on root directory");
        return;
    }

    if(deleteExtraction)	//extracted package is set to be deleted (by the creator of the PFW)
    {
        DWORD ret = 0;
        if(DirectoryExists(extractPath, ret))
        {
            LOG(Info, "Extracted package exists. Deleting.."); //presumably because user changed extraction destination
            DeleteFileOrDirectoryAlways(extractPath);
        }

        //cleaning up the footprint created by this extraction
        if( !newFootprint.IsEmpty() )
        {
            UString tempPathVar = extractPath;
            //we are deleting bottom-up upto 'newFootprint'
            while(tempPathVar.Find((LPCWSTR)newFootprint) == 0)
            {
                if(DirectoryExists(tempPathVar, ret))	//it is possible that part (or whole) of the extraction footprint was already deleted
                {
                    //delete only if the folder is empty, bail otherwise
                    if( PathIsDirectoryEmpty(tempPathVar) )
                    {
                        LOG(Info, "Deleting " + static_cast<LPCWSTR>(tempPathVar));
                        DeleteFileOrDirectoryAlways( tempPathVar );
                        if(tempPathVar.IsEqualTo_NoCase(static_cast<LPCWSTR>(newFootprint)))	//the last (level) folder that should ever be deleted
                        {
                            LOG(Info, "Extraction footprint completely deleted: " + static_cast<LPCWSTR>(newFootprint));
                            break;
                        }
                    }
                    else
                    {
                        LOG(Info, "Non-empty folder, terminating clean-up routine: " + static_cast<LPCWSTR>(tempPathVar));
                        break;
                    }
                }
                UString oneDirectoryUp = GetParentDirectory(tempPathVar);
                if(oneDirectoryUp.IsEqualTo_NoCase(static_cast<LPCWSTR>(tempPathVar)))	//no more folders to go up
                    break;
                else
                    tempPathVar = oneDirectoryUp;
            }
            LOG(Info, "Cleanup complete");
        }
    }
}

#include <new.h>
int sfx_new_handler( size_t size )
{
    (size); // DW
    MessageBoxA( NULL, "Could not allocate memory", "7-Zip SFX", MB_OK|MB_ICONSTOP );
    return 0;
}

class CCurrDirDll
{
private:
    CObjectVector<HMODULE> m_hModules;
    WCHAR m_systemDir[MAX_PATH];
    WCHAR m_windowsDir[MAX_PATH];

public:
    CCurrDirDll()
    {
        //Get system and Windows Directory
        GetSystemDirectory(m_systemDir,MAX_PATH);
        GetWindowsDirectory(m_windowsDir,MAX_PATH);
    }

    ~CCurrDirDll()
    {
        for(int i = 0; i < m_hModules.Size(); i++)
        {
            if(m_hModules[i] != NULL)
            {
                ::FreeLibrary(m_hModules[i]);
                m_hModules[i] = NULL;
            }
        }
    }

    void Load(void)
    {
        HANDLE hFind;
        WIN32_FIND_DATA data = {0};

        WCHAR currentDirPath[MAX_PATH];
        GetCurrentDirectory(MAX_PATH,currentDirPath);

        std::wstring currentDirSearchQuery(std::wstring(currentDirPath) + std::wstring(L"\\*.dll"));

        //find all dlls in current folder
        hFind = FindFirstFile(currentDirSearchQuery.c_str(), &data);
        if (hFind != INVALID_HANDLE_VALUE)
        {
            do
            {
                LoadDll(data);

            } while (FindNextFile(hFind, &data));

            FindClose(hFind);
        }
    }

private:
    void LoadDll(const WIN32_FIND_DATA& data)
    {
        std::wstring dllFileName(data.cFileName);

        if(dllFileName.empty())	return;

        HMODULE hModule = NULL;

        std::wstring dllLoadFrom = std::wstring(m_systemDir) + std::wstring(L"\\") + dllFileName;

        //check if dll present in Syste32, else check for Windows
        if(INVALID_FILE_ATTRIBUTES == GetFileAttributes(dllLoadFrom.c_str()))
        {
            LOG(Warning, "Dll not found at " + dllLoadFrom);

            dllLoadFrom = std::wstring(m_windowsDir) + std::wstring(L"\\") + dllFileName;

            if(INVALID_FILE_ATTRIBUTES == GetFileAttributes(dllLoadFrom.c_str()))
            {
                LOG(Warning, "Dll not found at " + dllLoadFrom);
                return;
            }
        }

        hModule = LoadLibrary(dllLoadFrom.c_str());

        if(hModule)
            m_hModules.Add(hModule);

        LOG(Info, (hModule? std::wstring(L"Dll loaded") : std::wstring(L"Not able to load"))
            + std::wstring(L"dll from ") + dllLoadFrom);

    }
};

#ifdef _SFX_USE_CUSTOM_EXCEPTIONS
int APIENTRY WinMain2( HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine, int nCmdShow );

int APIENTRY WinMain( HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR lpCmdLine, int nCmdShow )
{
    try {
        return WinMain2( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
    }
    catch( ... ) {
        return ERRC_EXCEPTION;
    }
}
int APIENTRY WinMain2( HINSTANCE hInstance,
#else
int APIENTRY WinMain( HINSTANCE hInstance,
#endif // _SFX_USE_CUSTOM_EXCEPTIONS
                      HINSTANCE hPrevInstance,
                      LPSTR lpCmdLine, int nCmdShow )
{
    (hInstance);
    (lpCmdLine);
    (nCmdShow);
    LOG(Info, "SFX Main execution started");
    LPTSTR commandLine = ::GetCommandLine();
    UString commandLineAsString = commandLine;

    if(::GetModuleFileName( NULL, strModulePathName.GetBuf(MAX_PATH*2), MAX_PATH*2 ) == 0)
    {
        LOG(Error, "Failed to get module name");
        return ERRC_GET_PATHNAME;
    }
    strModulePathName.ReleaseBuf_CalcLen(MAX_PATH*2);
    LOG(Info, "Using self-extracting archive" + static_cast<LPCWSTR>(strModulePathName));
    strModulePathName.ReleaseBuf_SetEnd(strModulePathName.Len());

    bool secondStageBypassed = false;
    if(commandLineAsString.Find(TEXT("-skipStage2")) > 0)
    {
        LOG(Info, "Bypass 2-stage installation, got: -skipStage2");
        secondStageBypassed = true;
    }

	bool isGFNSelfUpdate = false;
	if(commandLineAsString.Find(TEXT("-custominvokerid:7")) > 0)
	{		
		isGFNSelfUpdate = true;
	}

    UString pfwApplicationDirectory = GetParentDirectory(strModulePathName);
    if(pfwApplicationDirectory != strModulePathName)
    {
        if(FilesInDirectory(pfwApplicationDirectory) == 1)
        {
            //Hack: 2nd-stage execution is determined by absence of co-resident files, this needs improvement!
            LOG(Info, "2-stage PFW execution begins");
            secondStageBypassed = true;
        }
    }
    else
    {
        LOG(Error, "Bypass 2-stage installation, unable to get the PFW Application directory");
        secondStageBypassed = true;
    }

    secondStageBypassed = true;
    if(!secondStageBypassed)
    {
        UString secondStagePFW;
        if(CreateSecondStage(strModulePathName, secondStagePFW))
        {
            LOG(Info, "stage-2 PFW successfully created" + static_cast<LPCWSTR>(secondStagePFW));

            STARTUPINFO si;
            PROCESS_INFORMATION pi;

            ZeroMemory(&si, sizeof(si));
            si.cb = sizeof(si);
            ZeroMemory(&pi, sizeof(pi));

            if(FALSE == CreateProcess(static_cast<LPCTSTR>(secondStagePFW), ::GetCommandLine(), NULL, NULL, FALSE,
                                      0, NULL, static_cast<LPCTSTR>(GetParentDirectory(secondStagePFW)), &si, &pi))
            {
                LOG(Error, "Launch of stage-2 PFW failed: " + GetLastError());
            }
            else
            {
                LOG(Info, "Launched stage-2 PFW, stage-1 PFW terminating");
                return ERRC_NONE;
            }
        }
        LOG(Info, "Continuing execution the old way, installing: " + static_cast<LPCWSTR>(strModulePathName));
    }

    //
    // Quick and dirty fix for now that  only checks  if
    // ask is to skip the secure load. The dirty part is
    // we only check the presence of  -skipSecureLoad as
    // a string and not as an indiviual parameter
    //
    CCurrDirDll currentDirDll;
    if(commandLineAsString.Find(TEXT("-skipSecureLoad")) <= 0)
    {
        LOG(Info, "Enabling secure dll load");
        currentDirDll.Load();
    }
    else
    {
        LOG(Info, "Skipping secure dll load");
    }

#ifdef USE_SPLASH
    LOG(Info, "Started with USE_SPLASH flag");
    g_pSplashCore = new Splash(hInstance);
#else
    g_pSplashCore = new SplashCore(hInstance);
#endif

    _set_new_handler( sfx_new_handler );
    CreateDummyWindow();

    OSVERSIONINFO versionInfo;
    versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
    if( ::GetVersionEx(&versionInfo) == FALSE ||
            versionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT ||
                versionInfo.dwMajorVersion < 5 )
    {
        LOG(Error, "Unsupported OS");
        return ERRC_PLATFORM;
    }

    kSignatureConfigStart[0] = kSignatureConfigEnd[0] = ';';

    int		ShortcutDefault = -1;
    int		DeleteDefault = -1;
    int		SelfDelete = -1;
    LPCWSTR	lpwszBatchInstall = L"\0";
    bool	fAssumeYes = false;
    bool	fUseAutoInstall = false;
    bool	fNoRun = false;
    bool	fShowHelp = false;
    LPCWSTR	lpwszValue;
    // Command line
    UString tmpstr;
    UString	SfxConfigFilename;
    UString overriddenExtractPathString;
    CObjectVector<CTextConfigPair> pairs;
    LPCWSTR	overriddenExtractPath = NULL;

    UString strCmdLine;
    strCmdLine = L' ';
    strCmdLine += LoadQuotedString( commandLine, tmpstr );
    ReplaceHexChars( strCmdLine );
    LPCWSTR str = strCmdLine;
    str++;

#ifdef _SFX_USE_LANG
    if( (lpwszValue = IsSfxSwitch( str, CMDLINE_SFXLANG )) != NULL && *lpwszValue == L':' )
    {
        long idLang=StringToLong( lpwszValue+1 );
        if( idLang > 0 && idLang <= 0xffff )
            idSfxLang = (LANGID)idLang;
        while( ((unsigned)*str) > L' ' ) str++;
        SKIP_WHITESPACES_W( str );
    }
#endif // _SFX_USE_LANG

    SfxInit();

    if( IsSfxSwitch( str, CMDLINE_SFXVERSION ) != NULL )
    {
        ShowSfxVersion();
        return ERRC_NONE;
    }

    if( (lpwszValue = IsSfxSwitch( str, CMDLINE_SFXTEST )) != NULL )
#ifdef _SFX_USE_TEST
    {
        if( lpwszValue[0] != L':' )
            return ERRC_SFXTEST;
        switch( lpwszValue[1]|L' ' )
        {
        case L'd':
            // dialogs test
            if( lpwszValue[1] == L'D' )
            {
                if( lpwszValue[2] != L':' )
                    return ERRC_SFXTEST;
                lpwszValue += 3;
                while( ((unsigned)*lpwszValue) > L' ' && *lpwszValue != L':' )
                {
                    TSD_Flags += *lpwszValue++;
                }
                if( TSD_Flags.IsEmpty() != false )
                    return ERRC_SFXTEST;
            }
            else
                lpwszValue += 2;
            TSD_ExtractTimeout = 0;
            if( *lpwszValue == L':' )
            {
                TSD_ExtractTimeout=StringToLong( lpwszValue+1 );
            }
            if( TSD_ExtractTimeout > 3600 || TSD_ExtractTimeout == 0 )
                    TSD_ExtractTimeout = 10;
            nTestModeType = TMT_DIALOGS;
            break;
        case L'a':
            nTestModeType = TMT_ARCHIVE;
            break;
        case L'c':
            if( lpwszValue[2] == L'c' )
                nTestModeType = TMT_CHECK_CONFIG;
            else
                nTestModeType = TMT_DIALOGS_TO_STDOUT;
            break;
        case L'v':
            return 0x4000 | VERSION_REVISION;
        case L'f':
            return 0x2000 | SFX_USE_DLL | METHOD_LZMA | METHOD_DEFLATE | METHOD_PPMD | METHOD_BCJ | METHOD_BCJ2;
        default:
            return ERRC_SFXTEST;
        }

        while( ((unsigned)*lpwszValue) > L' ' ) lpwszValue++;
        SKIP_WHITESPACES_W( lpwszValue );
        str = lpwszValue;

        if( (lpwszValue = IsSfxSwitch( str,CMDLINE_SFXCONFIG )) != NULL )
        {
            if( *lpwszValue == L':' ) lpwszValue++;
            SKIP_WHITESPACES_W( lpwszValue );
            tmpstr.Empty();
            str = LoadQuotedString( lpwszValue, tmpstr );
            if( nTestModeType != TMT_ARCHIVE )
                strModulePathName = tmpstr;
        }
    }
#else
    {
        return ERRC_SFXTEST;
    }
#endif // _SFX_USE_TEST

    strSfxFolder = strModulePathName;
    strSfxName = strModulePathName;
    int nPos = GetDirectorySeparatorPos( strModulePathName );
    if( nPos >= 0 )
    {
        strSfxFolder.ReleaseBuf_SetEnd( nPos );
        strSfxName = static_cast<LPCWSTR>(strModulePathName) + nPos + 1;
        strTitle = static_cast<LPCWSTR>(strModulePathName)+nPos+1;
        if( (nPos = strTitle.ReverseFind( L'.' )) > 0 )
            strTitle.ReleaseBuf_SetEnd( nPos );
        strErrorTitle = strTitle;
        strErrorTitle += GetLanguageString( STR_ERROR_SUFFIX );
        lpwszTitle = strTitle;
        lpwszErrorTitle = strErrorTitle;
    }

    //! Added by DW. Make sure we can handle redirection
    SfxDisableWow64FsRedirection();
    // Create input stream
    CSfxInStream *inStreamSpec = new CSfxInStream;
    CMyComPtr<IInStream> inStream(inStreamSpec);
    if( !inStreamSpec->Open(strModulePathName) )
    {
        SfxEnableWow64FsRedirection();
        LOG(Error, "Failed to open archive" + (LPCWSTR)strModulePathName); //DW
        return ERRC_OPEN_ARCHIVE;
    }
    SfxEnableWow64FsRedirection();

    // Read SFX config
    AString	config;

    AString strSignatureBegin;
    AString strSignatureEnd;

#ifdef _SFX_USE_LANG
    CreateLanguageSignature( idSfxLang, strSignatureBegin, strSignatureEnd );
#endif // _SFX_USE_LANG
    if( ReadConfig( inStream, strSignatureBegin, strSignatureEnd, config ) == false )
    {
        if( ReadConfig( inStream, kSignatureConfigStart, kSignatureConfigEnd, config ) == false )
        {
            LOG(Error, "SFX configuration read" + static_cast<LPCWSTR>(strModulePathName));
            return ERRC_READ_CONFIG;
        }
    }

#ifdef _SFX_USE_TEST
    if( nTestModeType == 0 && (lpwszValue = IsSfxSwitch( str,CMDLINE_SFXCONFIG )) != NULL )
#else
    if( (lpwszValue = IsSfxSwitch( str,CMDLINE_SFXCONFIG )) != NULL )
#endif // _SFX_USE_TEST
    {
        if( *lpwszValue == L':' ) lpwszValue++;
        SKIP_WHITESPACES_W( lpwszValue );
        if( *lpwszValue != L'\0' && SaveConfiguration( lpwszValue, config ) == false )
        {
            LOG(Error, "SFX configuration write" + static_cast<LPCWSTR>(strModulePathName));
            return ERRC_CONFIG_DATA;
        }
        return ERRC_NONE;
    }

    pairs.Clear();
    if( !config.IsEmpty() )
    {
        // Split SFX config into pairs
        if( GetTextConfig(config, pairs,false) == false )
            return ERRC_CONFIG_DATA;
    }
#ifdef _SFX_USE_TEST
    if( nTestModeType == TMT_CHECK_CONFIG )
        return ERRC_NONE;
#endif // _SFX_USE_TEST

    //
    LANGSTRING * pLangString = SfxLangStrings;
    while( pLangString->id != 0 )
    {
        WCHAR wszVarName[sizeof(SFX_STRING_FORMAT)/sizeof(WCHAR)+32];
        wsprintf( wszVarName, SFX_STRING_FORMAT, pLangString->id );
        CTextConfigPair var;
        var.ID = wszVarName;
        var.String = GetLanguageString( pLangString->id );
        Variables.Add( var );
        pLangString++;
    }

    SetConfigVariables( pairs );

    LOG(Info, "Parsing command line " + str);
    str--;
    while( true )
    {
        while( ((unsigned)*str) > L' ' ) str++;
        SKIP_WHITESPACES_W( str );
        if( str[0] != L'/' && str[0] != L'-' )
            break;
        // '-!'
        if( str[1] == L'!' && ((unsigned)str[2]) <= L' ' )
        {
            str += 2;
            SKIP_WHITESPACES_W( str );
            break;
        }

        // 'ai', 'aiX, 'aiXXX'
        if( IsCommandLineSwitch( str, L"ai" ) != FALSE )
        {
            // first check batch mode
            if( (str[3] >= L'0' && str[3] <= '9') ||
                (str[3] >= L'a' && str[3] <= 'z') ||
                (str[3] >= L'A' && str[3] <= 'Z') )
            {
                lpwszBatchInstall = str+3;
                fUseAutoInstall = fAssumeYes = true;
                continue;
            }
            // sample autoinstall
            if( ((unsigned)str[3]) <= L' ' )
            {
                lpwszBatchInstall = L"\0";
                fUseAutoInstall = fAssumeYes = true;
                continue;
            }
            break;
        }

        if( IsCommandLineSwitch( str, L"x" ) != FALSE )
        {
            overriddenExtractPathString.Empty();
            str = LoadQuotedString(str + 2, overriddenExtractPathString);
            overriddenExtractPath = static_cast<LPCWSTR>(overriddenExtractPathString);
            LOG(Info, "Extraction path overridden at run-time: " + overriddenExtractPath);
            continue;
        }

        // 'om' - Overwrite mode
        if( IsCommandLineSwitch( str, L"om" ) != FALSE )
        {
            OverrideConfigParam( CFG_OVERWRITE_MODE,str+3, pairs );
            continue;
        }

        // 'gm' - GUI mode
        if( IsCommandLineSwitch( str, L"gm" ) != FALSE )
        {
            if( str[3] >= L'0' && str[3] <= (L'0'+GUIMODE_MAX) && str[4] <= L' ' )
            {
                OverrideConfigParam( CFG_GUIMODE,str+3, pairs );
                continue;
            }
            break;
        }

        // 'gf' - GUI flags
        if( IsCommandLineSwitch( str, L"gf" ) != FALSE )
        {
            OverrideConfigParam( CFG_GUIFLAGS,str+3,pairs );
            continue;
        }

        // 'mf' - Misc flags
        if( IsCommandLineSwitch( str, L"mf" ) != FALSE )
        {
            OverrideConfigParam( CFG_MISCFLAGS,str+3,pairs );
            continue;
        }

        // 'sd' - self delete
        if( IsCommandLineSwitch( str, L"sd" ) != FALSE )
        {
            if( (str[3] == L'0' || str[3] == L'1') && ((unsigned)str[4]) <= L' ' )
            {
                OverrideConfigParam( CFG_SELFDELETE,str+3,pairs );
                continue;
            }
            break;
        }

        // 'nr' - no run
        if( IsCommandLineSwitch( str, L"nr" ) != FALSE )
        {
            if( ((unsigned)str[3]) <= L' ' )
            {
                fNoRun = true;
                continue;
            }
            break;
        }

        // 'fm' - finish message
        if( IsCommandLineSwitch( str, L"fm" ) != FALSE )
        {
            if( str[3] >= L'0' && str[3] <= L'9' )
            {
                FinishMessage = StringToLong( str+3 );
                continue;
            }
            break;
        }

        // assume 'yes'
        if( (str[1] == L'y' || str[1] == L'Y') && ((unsigned)str[2]) <= L' ' )
        {
            fAssumeYes = true;
            continue;
        }

        // force silent extraction with -s cmdline arg
        if( (str[1] == L's' || str[1] == L'S') && ((unsigned)str[2] <= L' ') )
        {
            fAssumeYes = true;		//'-y'

            UString gm2Mode = CFG_GUIMODE;
            gm2Mode += L"=\"2\"";
            ParseConfigOverride( gm2Mode, pairs );	//'-gm2'

            break;
        }

        if( (str[1] == L'?' || str[1] == L'h' || str[1] == L'H') && ((unsigned)str[2]) <= L' ' )
        {
        /*	fShowHelp = true;  */      //no "help/menu" yet, suppress dialog STR_DEFAULT_HELP_TEXT
            continue;
        }
        LPCWSTR pnext = ParseConfigOverride( str+1, pairs );
        if( pnext != NULL )
        {
            if( pnext == (LPCWSTR)1 )
                return ERR_CONFIG_CMDLINE;
            str = pnext;
            continue;
        }
        break;
    }

    SetConfigVariables( pairs );

    // extra environment variables
    int nIndex = 0;
    while( (lpwszValue = GetTextConfigValue( pairs, CFG_SETENVIRONMENT, &nIndex)) != NULL )
    {
        UString strEnvName = lpwszValue;
        nPos = strEnvName.Find( L'=');
        if( nPos <= 0 )
            break;
        strEnvName.ReleaseBuf_SetEnd( nPos );
        CTextConfigPair pair;
        pair.ID = strEnvName;
        pair.String = lpwszValue+nPos+1;
        Variables.Add( pair );
        nIndex++;
    }

    SetEnvironment();

    if( GUIFlags == -1 )
        GUIFlags = 0;

    if( fShowHelp != false )
    {
        if( (lpwszValue = GetTextConfigValue(pairs,CFG_HELP_TEXT)) == NULL )
                lpwszValue = GetLanguageString( STR_DEFAULT_HELP_TEXT );
        CSfxDialog_HelpText	dlg;
        dlg.Show( SD_OK|SD_ICONINFORMATION, lpwszTitle, lpwszValue );
        return ERRC_NONE;
    }

    if( fAssumeYes != false )
        GUIFlags &= (~GUIFLAGS_CONFIRM_CANCEL);

    CoInitialize(NULL);

    // Path to extract
    if( (lpwszValue = GetTextConfigValue(pairs,CFG_INSTALLPATH)) != NULL )
        extractPath = lpwszValue;

    ReplaceVariables(extractPath);
    defaultExtractPath = extractPath;
    if(overriddenExtractPath != NULL)
    {
        extractPath = overriddenExtractPath;
        ReplaceVariables(extractPath);
    }

    if((lpwszValue = GetTextConfigValue(pairs, CFG_DELETE)) != NULL)
        deleteExtraction = true;

#ifdef _SFX_USE_TEST
    if( TSD_Flags.IsEmpty() == false )
    {
        return TestSfxDialogs( pairs );
    }
    if( nTestModeType == TMT_DIALOGS_TO_STDOUT )
    {
        return TestSfxDialogsToStdout( pairs );
    }
#endif // _SFX_USE_TEST
    // Begin prompt
Loc_BeginPrompt:

    if(g_pSplashCore->CheckShowSplashScreen() == false && (lpwszValue = GetTextConfigValue(pairs,CFG_BEGINPROMPT)) != NULL )
    {
        if( fAssumeYes == false )
        {
            if( SfxBeginPrompt( lpwszTitle, lpwszValue ) == FALSE )
            {
                return ERRC_CANCELED;
            }
            if( GetKeyState(VK_SHIFT)&0x8000 )
                fUseAutoInstall = true;
        }
    }

    if(g_pSplashCore->CheckShowSplashScreen())
    {
		if (isGFNSelfUpdate)
		{
			g_pSplashCore->SetSelfUpdateMode(true);
		}
        g_pSplashCore->CreateSplashScreen();
    }

    // check autoinstall
    UString	executeName;
    if( fUseAutoInstall != false )
    {
        LPCWSTR p1 = lpwszBatchInstall;
        do {
            executeName = CFG_AUTOINSTALL;
            executeName += *p1;
            if( GetTextConfigValue( pairs, executeName ) == NULL )
            {
                // auto install command not found
                LOG(Error, "Autoinstall command not found");
                if(GUIMode != GUIMODE_HIDDEN)
                    SfxErrorDialog(FALSE, ERR_AUTOINSTALL_NOTFOUND, (LPCWSTR)executeName);
                return ERRC_BAD_AUTOINSTALL;
            }
            p1++;
        } while( (*p1 >= L'0' && *p1 <= L'9') ||
                 (*p1 >= L'a' && *p1 <= L'z') ||
                 (*p1 >= L'A' && *p1 <= L'Z') );
        executeName = CFG_AUTOINSTALL;
        executeName += *lpwszBatchInstall;
    }

    bool fUseExecuteFile = false;
    // check for 'ExecuteFile' (only if no 'autoinstall' commands)
    if( executeName.IsEmpty() != false && GetTextConfigValue( pairs, CFG_EXECUTEFILE ) != NULL )
    {
        executeName = CFG_EXECUTEFILE;
        fUseExecuteFile = true;
    }
    // if no 'ExecuteFile' - try 'RunProgram'
    if( executeName.IsEmpty() != false && GetTextConfigValue( pairs, CFG_RUNPROGRAM ) != NULL )
        executeName = CFG_RUNPROGRAM;

    if(extractPath.IsEmpty())
    {
        LOG(Info, "Extraction path not specified. Directing extraction to a temporary folder");
        SetExtractionPathToTemp(extractPath);
    }

    DWORD ret = 0;
    HANDLE extractMutexHandle = NULL;
    if(!CreateExtractionMutex(extractMutexHandle, ret))
        return ret;

    if(!AcquireExtractionMutex(extractMutexHandle, ret))
        return ret;

    int maxAttempts=24;
    for(int verificationAttempts=0; verificationAttempts<=maxAttempts; verificationAttempts++)
    {
        if(GUIMode != GUIMODE_HIDDEN)
        {
            if(verificationAttempts)
                if( (lpwszValue = GetTextConfigValue(pairs,CFG_BEGINPROMPT)) != NULL )
                    if(fAssumeYes == false)
                        if(SfxBeginPrompt(lpwszTitle, lpwszValue) == FALSE)
                        {
                            MutexRelease(extractMutexHandle);
                            return ERRC_CANCELED;
                        }

            // Path to extract
            if( fUseAutoInstall == false && fAssumeYes == false &&
                (GUIFlags&(GUIFLAGS_EXTRACT_PATH2|GUIFLAGS_EXTRACT_PATH1)) == GUIFLAGS_EXTRACT_PATH2 &&
                    SfxExtractPathDialog( lpwszExtractPathTitle, lpwszExtractPathText ) == FALSE )
            {
                MutexRelease(extractMutexHandle);
                if( fUseBackward != false )
                    goto Loc_BeginPrompt;
                return ERRC_CANCELED;
            }
        }

        LOG(Info, "verification attempt: " + (verificationAttempts+1));
        if(VerifyExtractionPath(extractPath, ret))
        {
            LOG(Info, "Verification complete, determining extraction footprint..");
            ret = 0;
            if(GetExtractionFootprint(extractPath, ret))
            {
                if(deleteExtraction)
                {
                    lpwszValue = GetTextConfigValue(pairs, CFG_INSTALLPATH);
                    if((lpwszValue == NULL) || (lpwszValue != extractPath))
                        OverrideConfigParam(CFG_DELETE, static_cast<LPCWSTR>(extractPath), pairs);
                }

                LOG(Info, "Creating folder tree upto the parent of the extraction path..");
                if(extractPath != newFootprint)
                {
                    if(CreateFolderTree(static_cast<LPCWSTR>(GetParentDirectory(extractPath))) == FALSE)
                    {
                        LOG(Error, "Creating folder tree failed. Exiting..");
                        if(GUIMode != GUIMODE_HIDDEN)
                        {
                            LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
                            lpwszErrorTitle = GetLanguageString(STR_GENERIC_ERROR);
                            SfxErrorDialog(FALSE, ERR_CREATE_FOLDER, static_cast<LPCWSTR>(extractPath));
                            lpwszErrorTitle = lpwszErrorTitleSave;
                        }
                        SfxCleanup();
                        MutexRelease(extractMutexHandle);
                        return ERR_CREATE_FOLDER;
                    }
                }
                if(DirectoryExists(GetParentDirectory(extractPath), ret))
                {
#ifdef NOUAC
                        LOG(Info, "Using NOUAC configuration");
                        LOG(Info, "Creating folder: " + static_cast<LPCWSTR>(extractPath));

                        if(::CreateDirectory(static_cast<LPCWSTR>(extractPath), NULL) != FALSE)
                        {
                            LOG(Info, "Folder created successfully : " + static_cast<LPCWSTR>(extractPath));
                        }
                        else
                        {
                            LOG(Error, "Failed creating folder : " + static_cast<LPCWSTR>(extractPath));
                            return ERR_CREATE_FOLDER;
                        }

#else
                    LOG(Info, "Creating secure folder: " + static_cast<LPCWSTR>(extractPath));
                    if(!CreateSecureFolder(static_cast<LPCWSTR>(extractPath), ret))
                    {
                        LOG(Error, "Failed to secure strong ACLs on extraction path: " + ret);
                        if(deleteExtraction)
                        {
                            LOG(Info, "Redirecting extraction to a temp folder");
                            SetExtractionPathToTemp(extractPath);
                            LOG(Info, "Creating folder tree upto the parent of the temp path: " + static_cast<LPCWSTR>(extractPath));
                            if(CreateFolderTree(static_cast<LPCWSTR>(GetParentDirectory(extractPath))) == FALSE)
                            {
                                LOG(Error, "Creating folder tree failed. Exiting..");
                                SfxCleanup();
                                MutexRelease(extractMutexHandle);
                                return ERR_CREATE_FOLDER;
                            }
                            LOG(Info, "Creating secure folder: " + static_cast<LPCWSTR>(extractPath));
                            if(!CreateSecureFolder(static_cast<LPCWSTR>(extractPath), ret))
                            {
                                LOG(Error, "Failed to secure strong ACLs on extraction path: " + ret);
                                SfxCleanup();
                                MutexRelease(extractMutexHandle);
                                return ERR_CREATE_FOLDER;
                            }
                        }
                        else
                        {
                            if(GUIMode != GUIMODE_HIDDEN)
                            {
                                LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
                                lpwszErrorTitle = L"Insufficient Permissions";
                                SfxErrorDialog(FALSE, ERR_CREATE_FOLDER, static_cast<LPCWSTR>(extractPath));
                                lpwszErrorTitle = lpwszErrorTitleSave;
                            }
                            SfxCleanup();
                            MutexRelease(extractMutexHandle);
                            return ERR_CREATE_FOLDER;
                        }
                    }
#endif
                }
                else
                {
                    LOG(Error, "Creating folder tree failed abnormally. Exiting..");
                    if(GUIMode != GUIMODE_HIDDEN)
                    {
                        LPCWSTR	lpwszErrorTitleSave = lpwszErrorTitle;
                        lpwszErrorTitle = GetLanguageString(STR_GENERIC_ERROR);
                        SfxErrorDialog(FALSE, ERR_CREATE_FOLDER, static_cast<LPCWSTR>(extractPath));
                        lpwszErrorTitle = lpwszErrorTitleSave;
                    }
                    SfxCleanup();
                    MutexRelease(extractMutexHandle);
                    return ERR_CREATE_FOLDER;
                }

                break;
            }
        }
        else
        {
            //verification failed, recoverable?
            if(ret)		//Nope, true failure
            {
                LOG(Info, "Verification of extraction path failed with error code: " + ret +". Aborting..");
                if(GUIMode != GUIMODE_HIDDEN)
                {
                    LPCWSTR lpwszErrorTitleSave = lpwszErrorTitle;
                    if(ret == ERROR_ACCESS_DENIED)  lpwszErrorTitle = L"Access Denied";
                    else                            lpwszErrorTitle = L"Invalid Path";
                    SfxErrorDialog(FALSE, ERR_CREATE_FOLDER, static_cast<LPCWSTR>(extractPath));
                    lpwszErrorTitle = lpwszErrorTitleSave;
                }
                SfxCleanup();
                MutexRelease(extractMutexHandle);
                return ret;
            }
            if(GUIMode == GUIMODE_HIDDEN)	//no user inputs, so nothing to re-verify if in silent mode
            {
                LOG(Info, "Unknown error during verification of extraction path. Aborting..");
                SfxCleanup();
                MutexRelease(extractMutexHandle);
                return ERR_7Z_EXTRACT_ERROR2;
            }
        }

        if(verificationAttempts>=maxAttempts)
        {
            LOG(Info, "Verification of extraction path reached it maximum attempts, aborting..");
            SfxCleanup();
            MutexRelease(extractMutexHandle);
            return ERR_7Z_EXTRACT_ERROR2; //TODO: better error code!
        }
    }

    if( fAssumeYes != false )
        MiscFlags |= MISCFLAGS_NO_CHECK_DISK_FREE | MISCFLAGS_NO_CHECK_RAM;
#ifdef _SFX_USE_TEST
    HRESULT hrStatus;
    if( TSD_ExtractTimeout == 0 )
        hrStatus = ExtractArchive( inStream, extractPath );
    else
    {
        if( ExtractDialog() != FALSE )
            hrStatus = S_OK;
        else
            hrStatus = E_FAIL;
    }
#else
    HRESULT hrStatus = ExtractArchive( inStream, extractPath );
#endif // _SFX_USE_TEST

    // delete temporary folder
    if( hrStatus != S_OK )
    {
        LOG(Error, "Extraction failed with" + hrStatus);
        SfxCleanup();
        MutexRelease(extractMutexHandle);
        return ERRC_EXTRACT;
    }

    //
    SetEnvironment();

    DWORD exitCode = ERRC_NONE;
    if( fNoRun == false || fUseInstallPath == false )
    {
        LOG(Info, "Preparing to execute program: " + static_cast<LPCWSTR>(executeName));
        // execute program
        int	nExecuteIndex = 0;
        UString	ustrDirPrefix;
        if( fUseAutoInstall == false )
            GetConfigDirectory( pairs, ustrDirPrefix );
        bool fUsedSetupExe = false;
        LPCWSTR p1 = lpwszBatchInstall;
        while( 1 )
        {
            #ifdef _SFX_USE_PREFIX_PLATFORM
                int	nPlatform = SFX_EXECUTE_PLATFORM_ANY;
            #endif // _SFX_USE_PREFIX_PLATFORM
            bool	fUseHidcon = false;
            bool	fUseNoWait = false;
            UString	ustrRunProgram;
            UString	ustrConfigString;

            if( executeName.IsEmpty() != false )
            {
                if( fUseInstallPath != false )
                    break;
                lpwszValue = L"setup.exe";
                ustrRunProgram = extractPath + L"\\" + lpwszValue;
                if( ::GetFileAttributes( ustrRunProgram ) == (DWORD)-1 )
                {
                    // nothing to execute
                    LOG(Error, "No program found to execute");
                    SfxCleanup();
                    if(GUIMode != GUIMODE_HIDDEN)
                        SfxErrorDialog(FALSE, ERR_NO_SETUP_EXE);
                    MutexRelease(extractMutexHandle);
                    return ERRC_NOTHING_TO_EXECUTE;
                }
                fUsedSetupExe = true;
            }
            else
            {
                //
                if( (lpwszValue = GetTextConfigValue( pairs, executeName, &nExecuteIndex )) == NULL )
                {
                    p1++;
                    if( (*p1 >= L'0' && *p1 <= L'9') ||
                        (*p1 >= L'a' && *p1 <= L'z') ||
                        (*p1 >= L'A' && *p1 <= L'Z') )
                    {
                        executeName = CFG_AUTOINSTALL;
                        executeName += *p1;
                        nExecuteIndex = 0;
                        continue;
                    }
                    break;
                }
            }
            ustrConfigString = lpwszValue;
            ReplaceVariablesEx( ustrConfigString );
            lpwszValue = ustrConfigString;

            while( 1 )
            {
                LPCWSTR lpwszTmp;
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"hidcon", CPF_NONE )) != NULL )
                {
                    lpwszValue = lpwszTmp;
                    fUseHidcon = true;
                    continue;
                }
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"nowait", CPF_NONE )) != NULL )
                {
                    lpwszValue = lpwszTmp;
                    fUseNoWait = fUseInstallPath; // work only with InstallPath
                    continue;
                }
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"forcenowait", CPF_NONE )) != NULL )
                {
                    lpwszValue = lpwszTmp;
                    fUseNoWait = true;
                    continue;
                }
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"fm", CPF_NUMBER )) != NULL )
                {
                    if( FinishMessage == -1 ) FinishMessage = StringToLong( lpwszValue+2 );
                    lpwszValue = lpwszTmp;
                    continue;
                }
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"shc", CPF_SFX_INDEX )) != NULL )
                {
                    ShortcutDefault = lpwszValue[3]-L'0';
                    lpwszValue = lpwszTmp;
                    continue;
                }
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"del", CPF_SFX_INDEX )) != NULL )
                {
                    DeleteDefault = lpwszValue[3]-L'0';
                    lpwszValue = lpwszTmp;
                    continue;
                }
#ifdef _SFX_USE_PREFIX_PLATFORM
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"x86", CPF_NONE )) != NULL ||
                        (lpwszTmp = CheckPrefix( lpwszValue, L"i386", CPF_NONE )) != NULL )
                {
                    lpwszValue = lpwszTmp;
                    nPlatform = SFX_EXECUTE_PLATFORM_I386;
                    continue;
                }
                if( (lpwszTmp = CheckPrefix( lpwszValue, L"amd64", CPF_NONE )) != NULL ||
                        (lpwszTmp = CheckPrefix( lpwszValue, L"x64", CPF_NONE )) != NULL )
                {
                    lpwszValue = lpwszTmp;
                    nPlatform = SFX_EXECUTE_PLATFORM_AMD64;
                    continue;
                }
#endif // _SFX_USE_PREFIX_PLATFORM
                break;
            }

            if( fUseExecuteFile != false )
            {
                if( *lpwszValue != L'\"' )
                {
                    ustrRunProgram = L"\""; ustrRunProgram += lpwszValue; ustrRunProgram += L"\"";
                }
                else
                    ustrRunProgram = lpwszValue;
                if( (lpwszValue = GetTextConfigValue( pairs, CFG_EXECUTEPARAMETERS )) != NULL )
                {
                    ustrRunProgram += L" "; ustrRunProgram += lpwszValue;
                }
            }
            else
            {
                // 'RunProgram' or 'AutoInstall'
                ustrRunProgram = ustrDirPrefix; ustrRunProgram += lpwszValue;
            }

            UString filePath;
            UString fileParams;
            SHELLEXECUTEINFO execInfo;
            memset( &execInfo, 0, sizeof(execInfo) );
            execInfo.cbSize = sizeof(execInfo);
            execInfo.lpDirectory = extractPath;

            execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI;
            execInfo.nShow = SW_SHOWNORMAL;

            // first of all check for hidcon
            if( fUseHidcon != false )
            {
                // use hidcon
                execInfo.nShow = SW_HIDE;
                execInfo.fMask |= SEE_MASK_NO_CONSOLE;
            }

            // second: fetch filename
            fileParams = LoadQuotedString( ustrRunProgram, filePath );
            ReplaceVariablesEx( filePath );
            execInfo.lpFile = filePath;
#ifdef _SFX_USE_TEST
            if( nTestModeType == 0 )
            {
#endif // _SFX_USE_TEST
#ifdef _SFX_USE_PREFIX_PLATFORM
            if( SfxPrepareExecute( nPlatform ) != FALSE )
            {
#endif // _SFX_USE_PREFIX_PLATFORM
                if( *str != L'\0' )
                {
                    fileParams += L' ';
                    fileParams += str;
                    while( *str != L'\0' ) str++;
                }
                ReplaceVariablesEx( fileParams );
                if( fileParams.IsEmpty() == false )
                {
                    UString match(TEXT("^"));
                    UString replace(TEXT("\""));
                    fileParams.Replace(match, replace);
                    execInfo.lpParameters = fileParams;
                    LOG(Info, "execInfo.lpParameters: " + static_cast<LPCWSTR>(execInfo.lpParameters));
                    LOG(Info, "fileParams: " + static_cast<LPCWSTR>(fileParams));
                    execInfo.lpParameters = fileParams;
                }
                LOG(Info, "Preparing to execute program");
                if( ::ShellExecuteEx( &execInfo ) != FALSE )
                {
                    //g_pSplashCore->CloseSplashScreen();

                    if( fUseNoWait == false )
                    {
                        ::WaitForSingleObject( execInfo.hProcess, INFINITE );
                        if( ::GetExitCodeProcess( execInfo.hProcess, &exitCode ) == FALSE )
                        {
                            LOG(Info, "Unable to fetch the return code from " + static_cast<LPCWSTR>(execInfo.lpFile));
                            exitCode = ERRC_EXIT_CODE_UNAVAILABLE;
                        }
                        else
                            LOG(Info, "Exit code " + exitCode + " returned by " + static_cast<LPCWSTR>(execInfo.lpFile));
                    }
                    LOG(Info, "Execution succeeded");
                    g_pSplashCore->CloseSplashScreen();
                    ::CloseHandle( execInfo.hProcess );
                }
                else
                {
                    LOG(Error, "Execution failed");
                    if(GUIMode != GUIMODE_HIDDEN)
                        SfxErrorDialog(TRUE, ERR_EXECUTE, (LPCWSTR)ustrRunProgram);
                    SfxCleanup();
                    MutexRelease(extractMutexHandle);
                    return ERRC_EXECUTE;
                }
#ifdef _SFX_USE_PREFIX_PLATFORM
                SfxFinalizeExecute();
            }

#endif // _SFX_USE_PREFIX_PLATFORM
#ifdef _SFX_USE_TEST
            }
#endif // _SFX_USE_TEST
            if( fUseExecuteFile != false || fUsedSetupExe != false )
                break;
            ustrDirPrefix.Empty();
            nExecuteIndex++;
        }

#ifdef _SFX_USE_TEST
        if( nTestModeType == 0 )
        {
#endif // _SFX_USE_TEST
        // create shortcuts
#define SH_TEST
        SetEnvironment();
        ProcessPostExecuteSub( PostExecute_Shortcut, pairs, CFG_SHORTCUT, lpwszBatchInstall, ShortcutDefault );

        ::SetCurrentDirectory( strSfxFolder );
        ProcessPostExecuteSub( PostExecute_Delete, pairs, CFG_DELETE, lpwszBatchInstall, DeleteDefault );

        SfxCleanup();
        MutexRelease(extractMutexHandle);
#ifdef _SFX_USE_TEST
        }
#endif // _SFX_USE_TEST
    }
    if( FinishMessage == -1 && fAssumeYes == false )
    {
            FinishMessage = 1;
    }
    if( FinishMessage > 0 && (lpwszValue = GetTextConfigValue( pairs, CFG_FINISHMESSAGE )) != NULL )
    {
        if( FinishMessage > FINISHMESSAGE_MAX_TIMEOUT )
            FinishMessage = FINISHMESSAGE_MAX_TIMEOUT;
        CSfxDialog_FinishMessage	dlg;
        LOG(Info, "Showing finished dialog"); //DW
        dlg.Show( SD_OK|SD_ICONINFORMATION, lpwszTitle, lpwszValue );
    }

    // self delete
#ifdef _SFX_USE_TEST
    if( fNoRun == false && nTestModeType == 0 )
#else
    if( fNoRun == false )
#endif // _SFX_USE_TEST
    {
        switch( SelfDelete )
        {
        case 0:
            break;
        case 1:
            DeleteSFX( strModulePathName );
            break;
        default:
            if( (lpwszValue = GetTextConfigValue( pairs, CFG_SELFDELETE)) != NULL && lpwszValue[0] == L'1' )
                DeleteSFX( strModulePathName );
            break;
        }
    }
    LOG(Info, "All steps completed successfully");
    return exitCode;
}

HRESULT ExtractArchive( IInStream * inStream, const UString &folderName )
{
    LOG(Info, "Extracting archive to" + (LPCWSTR)folderName);
    HRESULT	result;
    CMyComPtr<IInArchive> archive;

    CrcGenerateTable();
    archive = new NArchive::N7z::CHandler;
    inStream->Seek( 0, STREAM_SEEK_SET, NULL );
    if( (result = archive->Open( inStream, &kMaxCheckStartPosition, NULL )) != S_OK )
    {
        if(GUIMode != GUIMODE_HIDDEN)
                SfxErrorDialog(FALSE, ERR_NON7Z_ARCHIVE);
        return E_FAIL;
    }

#ifdef _SFX_USE_TEST
    if( nTestModeType != TMT_ARCHIVE && CreateFolderTree( (LPCWSTR)folderName ) == FALSE )
        return E_FAIL;
#else
    if( CreateFolderTree( (LPCWSTR)folderName ) == FALSE )
        return E_FAIL;
#endif // _SFX_USE_TEST

    CSfxExtractEngine * extractEngine = new CSfxExtractEngine;
    result = extractEngine->Extract( archive, folderName );
    LOG(Info, "Extraction returned" + result);
    return result;
}

void DeleteSFX( UString moduleName )
{
    LOG(Info, "Deleting SFX archive"); //DW
    WCHAR	szRoot[4];
    if( moduleName[1] == L':' && (moduleName[2] == L'\\' || moduleName[2] == L'/') )
    {
        szRoot[0] = moduleName[0];
        szRoot[1] = L':'; szRoot[2] = L'\\'; szRoot[3] = L'\0';
        if( ::GetDriveType(szRoot) != DRIVE_FIXED )
            return;
    }
    else
    {
        return;
    }

    UString tmpPath = CreateTempName( L"7ZSfx%03x.cmd");

    LOG(Info, "Creating" + (LPCWSTR)tmpPath + "for deletion work"); //DW
    HANDLE hfCmd = ::CreateFile( tmpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
    if( hfCmd != INVALID_HANDLE_VALUE )
    {
        UString cmdBody;
        cmdBody = L":Repeat\r\n";
        cmdBody += L"del \""; cmdBody += moduleName; cmdBody += L"\"\r\n";
        cmdBody += L"if exist \""; cmdBody += moduleName; cmdBody += L"\" goto Repeat\r\n";
        cmdBody += L"del \""; cmdBody += tmpPath; cmdBody += L"\"\r\n";
        DWORD dwWrited;
        LOG(Info, "Writing contents as" + (LPCWSTR)cmdBody); //DW
        BOOL fWrited = WriteFile( hfCmd, UnicodeStringToMultiByte(cmdBody, CP_OEMCP), cmdBody.Len(), &dwWrited, NULL );
        ::CloseHandle( hfCmd );
        if( fWrited == FALSE || dwWrited != (DWORD)cmdBody.Len() )
        {
            DeleteFileOrDirectoryAlways( tmpPath );
            return;
        }
        ClearFileAttributes( moduleName );
        // execute cmd
        LOG(Info, "Executing" + (LPCWSTR)tmpPath + "for deletion work"); //DW
        ShellExecute( NULL, L"open", tmpPath, NULL, NULL, SW_HIDE );
    }
}
