#include #include #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"); } }