windows-nt/Source/XPSP1/NT/base/ntsetup/oemtools/oempatch/main.cpp
2020-09-26 16:20:57 +08:00

659 lines
18 KiB
C++

#include <windows.h>
#include <stdio.h>
#include "patchapi.h"
#include "const.h"
#include "ansparse.h"
#include "dirwak2a.h"
#include "filetree.h"
// the log file handle
HANDLE g_hDebugFile = INVALID_HANDLE_VALUE;
// is log required?
BOOL g_blnDebugFile = FALSE;
// is the logfile a new file?
BOOL g_blnDebugNewFile = FALSE;
// guard for the logfile access
CRITICAL_SECTION g_CSLogFile;
// multi thread support, default number of threads
ULONG g_iNumberOfThreads = 1;
// some patching options
DWORD g_iBestMethod = (PATCH_OPTION_USE_LZX_A | PATCH_OPTION_NO_TIMESTAMP);
BOOL g_blnCollectStat = FALSE;
BOOL g_blnFullLog = FALSE;
// some local function declarations
VOID DisplayHelpMessage(VOID);
VOID DisplayHelp(VOID);
VOID GenerateAnswerFile(VOID);
BOOL ParseCommandLine(IN INT argc, IN WCHAR* argv[]);
///////////////////////////////////////////////////////////////////////////////
//
// wmain, the unicode command line parameter entry point for this patching
// tool, it parses the commandline first, then reads the answerfile for
// what to do, and then creates the filetrees for languages that are to
// to matched and patched
//
///////////////////////////////////////////////////////////////////////////////
extern "C" int __cdecl wmain(int argc, WCHAR *argv[])
{
INT rc = PREP_NO_ERROR;
FileTree* pBaseTree = NULL;
FileTree* pLocTree = NULL;
AnswerParser* pParse = NULL;
PPATCH_LANGUAGE pBase = NULL;
PPATCH_LANGUAGE pLanguage = NULL;
InitializeCriticalSection(&g_CSLogFile);
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"Microsoft (R) OEMPatch Version %d.%.3d\nCopyright (C) Microsoft Corp 1999-2000. All rights reserved.",
g_iMajorVersion, g_iMinorVersion);
// parse command line
if(!ParseCommandLine(argc, argv))
{
goto CLEANUP;
}
DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
L"OEMPatch has finished parsing command line parameters.");
// create the parser for parsing the answer file
pParse = new AnswerParser;
if(pParse == NULL)
{
rc = PREP_NO_MEMORY;
goto CLEANUP;
}
DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
L"Creating the parser for OEMPatch.ans.");
// parsing the answer file
if(!pParse->Parse(ANS_FILE_NAME))
{
delete pParse;
pParse = NULL;
rc = PREP_INPUT_FILE_ERROR;
goto CLEANUP;
}
DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
L"Parsing answer file completed.");
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"Starting a new OEMPatch session.");
// call for filetree objects
// base language, any error causes the process to terminate, cannot go on without a base language
pBase = pParse->GetBaseLanguage();
if(pBase)
{
DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
L"Retrieved the base language.");
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"Creating base tree...");
rc = FileTree::Create(pBase, pParse, &pBaseTree, TRUE,
g_iBestMethod, g_blnCollectStat, g_blnFullLog);
if(rc != PREP_NO_ERROR)
{
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"error, CreateFileTree(\"%ls\") returned %d",
pBase->s_wszLanguage, rc);
goto CLEANUP;
}
DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
L"Done creating base tree.");
// create the multi-thread structs, done only once by the base tree
if(!pBaseTree->CreateMultiThreadStruct(g_iNumberOfThreads))
{
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"error, cannot create the patch thread(s)");
goto CLEANUP;
}
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"Loading base tree...");
rc = pBaseTree->Load(NULL);
if(rc != PREP_NO_ERROR)
{
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"error, LoadFileTree(\"%ls\") returned %d",
pBase->s_wszLanguage, rc);
goto CLEANUP;
}
DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
L"Done loading base tree.");
}
else
{
rc = PREP_INPUT_FILE_ERROR;
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"error, there is no base lanugage to load");
goto CLEANUP;
}
// none-base languages, errors are just recorded, continue to the next language
while((pLanguage = pParse->GetNextLanguage()) != NULL)
{
// none-base tree
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"Creating localized (\"%ls\") tree...",
pLanguage->s_wszDirectory);
rc = FileTree::Create(pLanguage, pParse, &pLocTree, FALSE,
g_iBestMethod, g_blnCollectStat, g_blnFullLog);
if(rc == PREP_NO_ERROR)
{
DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
L"Done creating localized (\"%ls\") tree.",
pLanguage->s_wszDirectory);
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"Loading and patching localized (\"%ls\") tree...", pLanguage->s_wszDirectory);
// load and match it
rc = pLocTree->Load(pBaseTree);
if(rc == PREP_NO_ERROR)
{
DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
L"Done loading and patching localized (\"%ls\") tree.", pLanguage->s_wszDirectory);
}
else
{
DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
L"warning, error in matching localized (\"%ls\") tree.", pLanguage->s_wszDirectory);
}
// remove for the next language
delete pLocTree;
pLocTree = NULL;
DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
L"Localized tree is removed from memory.");
}
else
{
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"error, creating localized (\"%ls\") tree.",
pLanguage->s_wszDirectory);
}
}
CLEANUP:
if(pBaseTree)
{
// remove the multi-thread struct here, done only once
pBaseTree->DeleteMultiThreadStruct();
delete pBaseTree;
pBaseTree = NULL;
}
if(pLocTree)
{
delete pLocTree;
pLocTree = NULL;
}
if(pParse)
{
delete pParse;
pParse = NULL;
}
if(rc != PREP_NO_ERROR)
{
switch(rc)
{
case PREP_NO_MEMORY:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"More memory is needed on this system to run.");
break;
case PREP_BAD_PATH_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Directory for some files are invalid, please check for valid directories.");
break;
case PREP_UNKNOWN_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Unknown error, contact support.");
break;
case PREP_DEPTH_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"File tree is too deep, contact support.");
break;
case PREP_BAD_COMMAND:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Bad command, try again.");
break;
case PREP_HASH_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Internal hashing error, contact support.");
break;
case PREP_BUFFER_OVERFLOW:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Internal buffer overflow, contact support.");
break;
case PREP_NOT_PATCHABLE:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Patching error, try again or contact support");
break;
case PREP_INPUT_FILE_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Input file error, please check for valid OEMPatch.ans.");
break;
case PREP_SCRIPT_FILE_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Cannot save infomation to scipt file, try again or contact support.");
break;
case PREP_PATCH_FILE_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Patch file error, try again or contact support.");
break;
case PREP_DIRECTORY_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"Directory error, try again or contact support.");
break;
case PREP_COPY_FILE_ERROR:
DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
L"File cannot be copied, check for available space and file permission.");
break;
}
}
DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
L"OEMPatch has finished.");
DisplayDebugMessage(FALSE, FALSE, TRUE, FALSE, NULL);
if(g_hDebugFile != INVALID_HANDLE_VALUE)
{
CloseHandle(g_hDebugFile);
}
DeleteCriticalSection(&g_CSLogFile);
return(rc);
}
///////////////////////////////////////////////////////////////////////////////
//
// ParseCommandLine, this function attempts to parse the commandline parameters
// and saves them into global variables for easy access
//
// Parameters:
//
// argc, the number of parameter in the command line
// argv[], the command line buffer, holds all the command line parameters
//
// Return:
//
// TRUE for correct command line parameters, FALSE for invalid parameters
//
///////////////////////////////////////////////////////////////////////////////
BOOL ParseCommandLine(IN INT argc, IN WCHAR* argv[])
{
BOOL blnReturn = TRUE;
// parse commandline parameters
for(INT i = 1; i < argc && blnReturn; i++)
{
if(argv[i][0] == L'/')
{
switch(argv[i][1])
{
case L'm':
// multi thread
if(argv[i][2] >= L'1' && argv[i][2] <= '9')
{
g_iNumberOfThreads = argv[i][2] - L'0';
}
break;
case L's':
// collect all stats
g_blnCollectStat = TRUE;
break;
case L'b':
// best patch
g_iBestMethod |= PATCH_OPTION_FAIL_IF_BIGGER;
break;
case L'L':
// log a new file
g_blnDebugNewFile = TRUE;
case L'l':
// log a old file
g_blnDebugFile = TRUE;
if(g_blnDebugNewFile)
{
// new logfile
ULONG iWriteByte = 0;
g_hDebugFile = CreateFileW(LOG_FILE_NAME,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
if(g_hDebugFile != INVALID_HANDLE_VALUE)
{
WriteFile(g_hDebugFile, &UNICODE_HEAD, sizeof(WCHAR),
&iWriteByte, NULL);
}
}
else
{
// append to old logfile
g_hDebugFile = CreateFileW(LOG_FILE_NAME,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
if(g_hDebugFile != INVALID_HANDLE_VALUE)
{
SetFilePointer(g_hDebugFile, 0, NULL, FILE_END);
}
else
{
ULONG iWriteByte = 0;
g_hDebugFile = CreateFileW(LOG_FILE_NAME,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
if(g_hDebugFile != INVALID_HANDLE_VALUE)
{
WriteFile(g_hDebugFile, &UNICODE_HEAD,
sizeof(WCHAR), &iWriteByte, NULL);
}
}
}
if(g_hDebugFile == INVALID_HANDLE_VALUE)
{
printf("warning, Logfile error, failed to open OEMPatch.log.\n");
printf("warning, there is no log file\n");
}
if(argv[i][2] == L'C')
{
g_blnFullLog = TRUE;
}
break;
case L'g':
// generate a sample answerfile
GenerateAnswerFile();
blnReturn = FALSE;
break;
case L'?':
// show help
DisplayHelp();
blnReturn = FALSE;
break;
default:
// show the message for help
DisplayHelpMessage();
blnReturn = FALSE;
}
}
else
{
DisplayHelpMessage();
blnReturn = FALSE;
}
}
return(blnReturn);
}
///////////////////////////////////////////////////////////////////////////////
//
// DisplayDebugMessage, show the message to the logfile is possible, else if
// the message is intended as print, output to stdout.
// The function will buffer all the messages up until
// either flushed or the buffer overflows, the reason
// being is to reduce io to disk
//
// Note, all message in pwszWhat should not contain any endoflinec char,
// when blnFlush is TRUE, all other parameter are ignored
//
// Parameters:
//
// blnTime, time included in the message?
// blnBanner, create a banner around the message?
// blnFlush, write the buffer to file
// blnPrint, show this message to stdout?
// pwszWhat, the format string
// ..., parameters for the format string
//
// Return:
//
// none
//
///////////////////////////////////////////////////////////////////////////////
VOID DisplayDebugMessage(IN BOOL blnTime,
IN BOOL blnBanner,
IN BOOL blnFlush,
IN BOOL blnPrint,
IN WCHAR* pwszWhat,
...)
{
static WCHAR strWriteBuffer[SUPER_LENGTH];
static ULONG iSize;
WCHAR msg[STRING_LENGTH];
ZeroMemory(msg, STRING_LENGTH * sizeof(WCHAR));
ULONG iBegin = 0;
ULONG iLength = 0;
ULONG iWriteBytes = 0;
va_list arglist;
if(pwszWhat)
{
va_start(arglist, pwszWhat);
vswprintf(msg, pwszWhat, arglist);
va_end(arglist);
}
__try
{
EnterCriticalSection(&g_CSLogFile);
if(g_hDebugFile != INVALID_HANDLE_VALUE)
{
if(!blnFlush && pwszWhat)
{
// 2 for endofline and carriage return
iLength = wcslen(msg) + 2;
if(blnTime)
{
// 1 for space
iLength += 1 + TIME_LENGTH;
}
if(blnBanner)
{
iLength += BANNER_LENGTH * 2;
}
iBegin = iSize;
iSize += iLength;
if(iSize + 1 < SUPER_LENGTH)
{
// buffer the message up
if(blnBanner)
{
wcscat(strWriteBuffer, BANNER);
}
if(blnTime)
{
WCHAR strDate[LANGUAGE_LENGTH];
WCHAR strTime[LANGUAGE_LENGTH];
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
GetDateFormatW(LOCALE_USER_DEFAULT, 0, &SystemTime,
L"MMddyy", strDate, LANGUAGE_LENGTH);
GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &SystemTime,
L"HHmmss", strTime, LANGUAGE_LENGTH);
wcscat(strWriteBuffer, strDate);
wcscat(strWriteBuffer, strTime);
wcscat(strWriteBuffer, SPACE);
}
wcscat(strWriteBuffer, msg);
wcscat(strWriteBuffer, ENDOFLINE);
wcscat(strWriteBuffer, CRETURN);
if(blnBanner)
{
wcscat(strWriteBuffer, BANNER);
}
strWriteBuffer[iSize] = 0;
}
else
{
// overflow
WriteFile(g_hDebugFile, strWriteBuffer,
iBegin * sizeof(WCHAR), &iWriteBytes, NULL);
ZeroMemory(strWriteBuffer, SUPER_LENGTH * sizeof(WCHAR));
if(blnBanner)
{
wcscat(strWriteBuffer, BANNER);
}
if(blnTime)
{
WCHAR strDate[LANGUAGE_LENGTH];
WCHAR strTime[LANGUAGE_LENGTH];
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
GetDateFormatW(LOCALE_USER_DEFAULT, 0,
&SystemTime, L"MMddyy", strDate, LANGUAGE_LENGTH);
GetTimeFormatW(LOCALE_USER_DEFAULT,
0, &SystemTime, L"HHmmss", strTime,
LANGUAGE_LENGTH);
wcscat(strWriteBuffer, strDate);
wcscat(strWriteBuffer, strTime);
wcscat(strWriteBuffer, SPACE);
}
wcscat(strWriteBuffer, msg);
wcscat(strWriteBuffer, ENDOFLINE);
wcscat(strWriteBuffer, CRETURN);
if(blnBanner)
{
wcscat(strWriteBuffer, BANNER);
}
iSize = iLength;
}
}
else if(blnFlush && iSize > 0)
{
// flush the buffer to file
WriteFile(g_hDebugFile, strWriteBuffer, iSize * sizeof(WCHAR), &iWriteBytes, NULL);
iSize = 0;
}
if(blnPrint)
{
wprintf(L"%ls\n", msg);
}
}
else if(msg && blnPrint)
{
wprintf(L"%ls\n", msg);
}
}
__finally
{
LeaveCriticalSection(&g_CSLogFile);
}
}
///////////////////////////////////////////////////////////////////////////////
//
// DisplayHelpMessage, shows what to do if the user if confused
//
// Parameters:
//
// none
//
// Return:
//
// none
//
///////////////////////////////////////////////////////////////////////////////
VOID DisplayHelpMessage(VOID)
{
printf("\nFor help try /?.\n");
}
///////////////////////////////////////////////////////////////////////////////
//
// DisplayHelp, shows what oempatch is cabable of
//
// Parameters:
//
// none
//
// Return:
//
// none
//
///////////////////////////////////////////////////////////////////////////////
VOID DisplayHelp(VOID)
{
printf("\nOEMPatch %d.%.3d - Help\n", g_iMajorVersion, g_iMinorVersion);
printf("Usage: oempatch [/m#] [/s] [/b] [/l | /L [C] ] [/g | /?]\n");
printf("(no switch):\trun patch, single thread\n");
printf("/m#:\t\trun multi-threaded patch, maximum # is 9, minimum is 1\n");
printf("/s:\t\tcollect patching stats when running patch, slows down patching\n");
printf("/b:\t\tchoose best patching methods, slows down patching\n");
printf("/l:\t\trun patch with append to the log file OEMPatch.log\n");
printf("/L:\t\trun patch with a new log file OEMPatch.log\n");
printf("/lC, /LC:\tevery file status is logged\n");
printf("/g:\t\tgenerate a new sample answer file if there is no such file\n");
printf("/?:\t\tdisplay help\n");
}
///////////////////////////////////////////////////////////////////////////////
//
// DisplayHelp, creates a sample answer file, so that the user can modifie
// according to needs
//
// Parameters:
//
// none
//
// Return:
//
// none
//
///////////////////////////////////////////////////////////////////////////////
VOID GenerateAnswerFile(VOID)
{
HANDLE hAnsFile = INVALID_HANDLE_VALUE;
ULONG iWriteByte = 0;
ULONG i = 0;
hAnsFile = CreateFileW(ANS_FILE_NAME,
GENERIC_WRITE,
0,
(LPSECURITY_ATTRIBUTES)NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
if(hAnsFile != INVALID_HANDLE_VALUE)
{
// construct the answer file
WriteFile(hAnsFile, &UNICODE_HEAD, sizeof(WCHAR), &iWriteByte, NULL);
// SAMPLEFILE is defined in ansparse.h
while(SAMPLEFILE[i][0] &&
WriteFile(hAnsFile, SAMPLEFILE[i],
wcslen(SAMPLEFILE[i]) * sizeof(WCHAR), &iWriteByte, NULL))
{
i++;
}
CloseHandle(hAnsFile);
if(SAMPLEFILE[i][0])
{
printf("Warning:OEMPatch.ans may contain errors, needs regeneration.\n");
}
else
{
printf("OEMPatch.ans generated, needs to be manually completed and saved as UNICODE.\n");
}
}
else
{
printf("OEMPatch.ans already exists, no new file created.\n");
}
}