Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

658 lines
18 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include "patchapi.h"
  4. #include "const.h"
  5. #include "ansparse.h"
  6. #include "dirwak2a.h"
  7. #include "filetree.h"
  8. // the log file handle
  9. HANDLE g_hDebugFile = INVALID_HANDLE_VALUE;
  10. // is log required?
  11. BOOL g_blnDebugFile = FALSE;
  12. // is the logfile a new file?
  13. BOOL g_blnDebugNewFile = FALSE;
  14. // guard for the logfile access
  15. CRITICAL_SECTION g_CSLogFile;
  16. // multi thread support, default number of threads
  17. ULONG g_iNumberOfThreads = 1;
  18. // some patching options
  19. DWORD g_iBestMethod = (PATCH_OPTION_USE_LZX_A | PATCH_OPTION_NO_TIMESTAMP);
  20. BOOL g_blnCollectStat = FALSE;
  21. BOOL g_blnFullLog = FALSE;
  22. // some local function declarations
  23. VOID DisplayHelpMessage(VOID);
  24. VOID DisplayHelp(VOID);
  25. VOID GenerateAnswerFile(VOID);
  26. BOOL ParseCommandLine(IN INT argc, IN WCHAR* argv[]);
  27. ///////////////////////////////////////////////////////////////////////////////
  28. //
  29. // wmain, the unicode command line parameter entry point for this patching
  30. // tool, it parses the commandline first, then reads the answerfile for
  31. // what to do, and then creates the filetrees for languages that are to
  32. // to matched and patched
  33. //
  34. ///////////////////////////////////////////////////////////////////////////////
  35. extern "C" int __cdecl wmain(int argc, WCHAR *argv[])
  36. {
  37. INT rc = PREP_NO_ERROR;
  38. FileTree* pBaseTree = NULL;
  39. FileTree* pLocTree = NULL;
  40. AnswerParser* pParse = NULL;
  41. PPATCH_LANGUAGE pBase = NULL;
  42. PPATCH_LANGUAGE pLanguage = NULL;
  43. InitializeCriticalSection(&g_CSLogFile);
  44. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  45. L"Microsoft (R) OEMPatch Version %d.%.3d\nCopyright (C) Microsoft Corp 1999-2000. All rights reserved.",
  46. g_iMajorVersion, g_iMinorVersion);
  47. // parse command line
  48. if(!ParseCommandLine(argc, argv))
  49. {
  50. goto CLEANUP;
  51. }
  52. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  53. L"OEMPatch has finished parsing command line parameters.");
  54. // create the parser for parsing the answer file
  55. pParse = new AnswerParser;
  56. if(pParse == NULL)
  57. {
  58. rc = PREP_NO_MEMORY;
  59. goto CLEANUP;
  60. }
  61. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  62. L"Creating the parser for OEMPatch.ans.");
  63. // parsing the answer file
  64. if(!pParse->Parse(ANS_FILE_NAME))
  65. {
  66. delete pParse;
  67. pParse = NULL;
  68. rc = PREP_INPUT_FILE_ERROR;
  69. goto CLEANUP;
  70. }
  71. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  72. L"Parsing answer file completed.");
  73. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  74. L"Starting a new OEMPatch session.");
  75. // call for filetree objects
  76. // base language, any error causes the process to terminate, cannot go on without a base language
  77. pBase = pParse->GetBaseLanguage();
  78. if(pBase)
  79. {
  80. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  81. L"Retrieved the base language.");
  82. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  83. L"Creating base tree...");
  84. rc = FileTree::Create(pBase, pParse, &pBaseTree, TRUE,
  85. g_iBestMethod, g_blnCollectStat, g_blnFullLog);
  86. if(rc != PREP_NO_ERROR)
  87. {
  88. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  89. L"error, CreateFileTree(\"%ls\") returned %d",
  90. pBase->s_wszLanguage, rc);
  91. goto CLEANUP;
  92. }
  93. DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
  94. L"Done creating base tree.");
  95. // create the multi-thread structs, done only once by the base tree
  96. if(!pBaseTree->CreateMultiThreadStruct(g_iNumberOfThreads))
  97. {
  98. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  99. L"error, cannot create the patch thread(s)");
  100. goto CLEANUP;
  101. }
  102. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  103. L"Loading base tree...");
  104. rc = pBaseTree->Load(NULL);
  105. if(rc != PREP_NO_ERROR)
  106. {
  107. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  108. L"error, LoadFileTree(\"%ls\") returned %d",
  109. pBase->s_wszLanguage, rc);
  110. goto CLEANUP;
  111. }
  112. DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
  113. L"Done loading base tree.");
  114. }
  115. else
  116. {
  117. rc = PREP_INPUT_FILE_ERROR;
  118. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  119. L"error, there is no base lanugage to load");
  120. goto CLEANUP;
  121. }
  122. // none-base languages, errors are just recorded, continue to the next language
  123. while((pLanguage = pParse->GetNextLanguage()) != NULL)
  124. {
  125. // none-base tree
  126. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  127. L"Creating localized (\"%ls\") tree...",
  128. pLanguage->s_wszDirectory);
  129. rc = FileTree::Create(pLanguage, pParse, &pLocTree, FALSE,
  130. g_iBestMethod, g_blnCollectStat, g_blnFullLog);
  131. if(rc == PREP_NO_ERROR)
  132. {
  133. DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
  134. L"Done creating localized (\"%ls\") tree.",
  135. pLanguage->s_wszDirectory);
  136. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  137. L"Loading and patching localized (\"%ls\") tree...", pLanguage->s_wszDirectory);
  138. // load and match it
  139. rc = pLocTree->Load(pBaseTree);
  140. if(rc == PREP_NO_ERROR)
  141. {
  142. DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
  143. L"Done loading and patching localized (\"%ls\") tree.", pLanguage->s_wszDirectory);
  144. }
  145. else
  146. {
  147. DisplayDebugMessage(TRUE, FALSE, FALSE, TRUE,
  148. L"warning, error in matching localized (\"%ls\") tree.", pLanguage->s_wszDirectory);
  149. }
  150. // remove for the next language
  151. delete pLocTree;
  152. pLocTree = NULL;
  153. DisplayDebugMessage(FALSE, FALSE, FALSE, FALSE,
  154. L"Localized tree is removed from memory.");
  155. }
  156. else
  157. {
  158. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  159. L"error, creating localized (\"%ls\") tree.",
  160. pLanguage->s_wszDirectory);
  161. }
  162. }
  163. CLEANUP:
  164. if(pBaseTree)
  165. {
  166. // remove the multi-thread struct here, done only once
  167. pBaseTree->DeleteMultiThreadStruct();
  168. delete pBaseTree;
  169. pBaseTree = NULL;
  170. }
  171. if(pLocTree)
  172. {
  173. delete pLocTree;
  174. pLocTree = NULL;
  175. }
  176. if(pParse)
  177. {
  178. delete pParse;
  179. pParse = NULL;
  180. }
  181. if(rc != PREP_NO_ERROR)
  182. {
  183. switch(rc)
  184. {
  185. case PREP_NO_MEMORY:
  186. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  187. L"More memory is needed on this system to run.");
  188. break;
  189. case PREP_BAD_PATH_ERROR:
  190. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  191. L"Directory for some files are invalid, please check for valid directories.");
  192. break;
  193. case PREP_UNKNOWN_ERROR:
  194. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  195. L"Unknown error, contact support.");
  196. break;
  197. case PREP_DEPTH_ERROR:
  198. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  199. L"File tree is too deep, contact support.");
  200. break;
  201. case PREP_BAD_COMMAND:
  202. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  203. L"Bad command, try again.");
  204. break;
  205. case PREP_HASH_ERROR:
  206. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  207. L"Internal hashing error, contact support.");
  208. break;
  209. case PREP_BUFFER_OVERFLOW:
  210. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  211. L"Internal buffer overflow, contact support.");
  212. break;
  213. case PREP_NOT_PATCHABLE:
  214. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  215. L"Patching error, try again or contact support");
  216. break;
  217. case PREP_INPUT_FILE_ERROR:
  218. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  219. L"Input file error, please check for valid OEMPatch.ans.");
  220. break;
  221. case PREP_SCRIPT_FILE_ERROR:
  222. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  223. L"Cannot save infomation to scipt file, try again or contact support.");
  224. break;
  225. case PREP_PATCH_FILE_ERROR:
  226. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  227. L"Patch file error, try again or contact support.");
  228. break;
  229. case PREP_DIRECTORY_ERROR:
  230. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  231. L"Directory error, try again or contact support.");
  232. break;
  233. case PREP_COPY_FILE_ERROR:
  234. DisplayDebugMessage(FALSE, FALSE, FALSE, TRUE,
  235. L"File cannot be copied, check for available space and file permission.");
  236. break;
  237. }
  238. }
  239. DisplayDebugMessage(TRUE, TRUE, FALSE, TRUE,
  240. L"OEMPatch has finished.");
  241. DisplayDebugMessage(FALSE, FALSE, TRUE, FALSE, NULL);
  242. if(g_hDebugFile != INVALID_HANDLE_VALUE)
  243. {
  244. CloseHandle(g_hDebugFile);
  245. }
  246. DeleteCriticalSection(&g_CSLogFile);
  247. return(rc);
  248. }
  249. ///////////////////////////////////////////////////////////////////////////////
  250. //
  251. // ParseCommandLine, this function attempts to parse the commandline parameters
  252. // and saves them into global variables for easy access
  253. //
  254. // Parameters:
  255. //
  256. // argc, the number of parameter in the command line
  257. // argv[], the command line buffer, holds all the command line parameters
  258. //
  259. // Return:
  260. //
  261. // TRUE for correct command line parameters, FALSE for invalid parameters
  262. //
  263. ///////////////////////////////////////////////////////////////////////////////
  264. BOOL ParseCommandLine(IN INT argc, IN WCHAR* argv[])
  265. {
  266. BOOL blnReturn = TRUE;
  267. // parse commandline parameters
  268. for(INT i = 1; i < argc && blnReturn; i++)
  269. {
  270. if(argv[i][0] == L'/')
  271. {
  272. switch(argv[i][1])
  273. {
  274. case L'm':
  275. // multi thread
  276. if(argv[i][2] >= L'1' && argv[i][2] <= '9')
  277. {
  278. g_iNumberOfThreads = argv[i][2] - L'0';
  279. }
  280. break;
  281. case L's':
  282. // collect all stats
  283. g_blnCollectStat = TRUE;
  284. break;
  285. case L'b':
  286. // best patch
  287. g_iBestMethod |= PATCH_OPTION_FAIL_IF_BIGGER;
  288. break;
  289. case L'L':
  290. // log a new file
  291. g_blnDebugNewFile = TRUE;
  292. case L'l':
  293. // log a old file
  294. g_blnDebugFile = TRUE;
  295. if(g_blnDebugNewFile)
  296. {
  297. // new logfile
  298. ULONG iWriteByte = 0;
  299. g_hDebugFile = CreateFileW(LOG_FILE_NAME,
  300. GENERIC_WRITE,
  301. FILE_SHARE_WRITE | FILE_SHARE_READ,
  302. (LPSECURITY_ATTRIBUTES)NULL,
  303. CREATE_ALWAYS,
  304. FILE_ATTRIBUTE_NORMAL,
  305. (HANDLE)NULL);
  306. if(g_hDebugFile != INVALID_HANDLE_VALUE)
  307. {
  308. WriteFile(g_hDebugFile, &UNICODE_HEAD, sizeof(WCHAR),
  309. &iWriteByte, NULL);
  310. }
  311. }
  312. else
  313. {
  314. // append to old logfile
  315. g_hDebugFile = CreateFileW(LOG_FILE_NAME,
  316. GENERIC_WRITE,
  317. FILE_SHARE_WRITE | FILE_SHARE_READ,
  318. (LPSECURITY_ATTRIBUTES)NULL,
  319. OPEN_EXISTING,
  320. FILE_ATTRIBUTE_NORMAL,
  321. (HANDLE)NULL);
  322. if(g_hDebugFile != INVALID_HANDLE_VALUE)
  323. {
  324. SetFilePointer(g_hDebugFile, 0, NULL, FILE_END);
  325. }
  326. else
  327. {
  328. ULONG iWriteByte = 0;
  329. g_hDebugFile = CreateFileW(LOG_FILE_NAME,
  330. GENERIC_WRITE,
  331. FILE_SHARE_WRITE | FILE_SHARE_READ,
  332. (LPSECURITY_ATTRIBUTES)NULL,
  333. CREATE_ALWAYS,
  334. FILE_ATTRIBUTE_NORMAL,
  335. (HANDLE)NULL);
  336. if(g_hDebugFile != INVALID_HANDLE_VALUE)
  337. {
  338. WriteFile(g_hDebugFile, &UNICODE_HEAD,
  339. sizeof(WCHAR), &iWriteByte, NULL);
  340. }
  341. }
  342. }
  343. if(g_hDebugFile == INVALID_HANDLE_VALUE)
  344. {
  345. printf("warning, Logfile error, failed to open OEMPatch.log.\n");
  346. printf("warning, there is no log file\n");
  347. }
  348. if(argv[i][2] == L'C')
  349. {
  350. g_blnFullLog = TRUE;
  351. }
  352. break;
  353. case L'g':
  354. // generate a sample answerfile
  355. GenerateAnswerFile();
  356. blnReturn = FALSE;
  357. break;
  358. case L'?':
  359. // show help
  360. DisplayHelp();
  361. blnReturn = FALSE;
  362. break;
  363. default:
  364. // show the message for help
  365. DisplayHelpMessage();
  366. blnReturn = FALSE;
  367. }
  368. }
  369. else
  370. {
  371. DisplayHelpMessage();
  372. blnReturn = FALSE;
  373. }
  374. }
  375. return(blnReturn);
  376. }
  377. ///////////////////////////////////////////////////////////////////////////////
  378. //
  379. // DisplayDebugMessage, show the message to the logfile is possible, else if
  380. // the message is intended as print, output to stdout.
  381. // The function will buffer all the messages up until
  382. // either flushed or the buffer overflows, the reason
  383. // being is to reduce io to disk
  384. //
  385. // Note, all message in pwszWhat should not contain any endoflinec char,
  386. // when blnFlush is TRUE, all other parameter are ignored
  387. //
  388. // Parameters:
  389. //
  390. // blnTime, time included in the message?
  391. // blnBanner, create a banner around the message?
  392. // blnFlush, write the buffer to file
  393. // blnPrint, show this message to stdout?
  394. // pwszWhat, the format string
  395. // ..., parameters for the format string
  396. //
  397. // Return:
  398. //
  399. // none
  400. //
  401. ///////////////////////////////////////////////////////////////////////////////
  402. VOID DisplayDebugMessage(IN BOOL blnTime,
  403. IN BOOL blnBanner,
  404. IN BOOL blnFlush,
  405. IN BOOL blnPrint,
  406. IN WCHAR* pwszWhat,
  407. ...)
  408. {
  409. static WCHAR strWriteBuffer[SUPER_LENGTH];
  410. static ULONG iSize;
  411. WCHAR msg[STRING_LENGTH];
  412. ZeroMemory(msg, STRING_LENGTH * sizeof(WCHAR));
  413. ULONG iBegin = 0;
  414. ULONG iLength = 0;
  415. ULONG iWriteBytes = 0;
  416. va_list arglist;
  417. if(pwszWhat)
  418. {
  419. va_start(arglist, pwszWhat);
  420. vswprintf(msg, pwszWhat, arglist);
  421. va_end(arglist);
  422. }
  423. __try
  424. {
  425. EnterCriticalSection(&g_CSLogFile);
  426. if(g_hDebugFile != INVALID_HANDLE_VALUE)
  427. {
  428. if(!blnFlush && pwszWhat)
  429. {
  430. // 2 for endofline and carriage return
  431. iLength = wcslen(msg) + 2;
  432. if(blnTime)
  433. {
  434. // 1 for space
  435. iLength += 1 + TIME_LENGTH;
  436. }
  437. if(blnBanner)
  438. {
  439. iLength += BANNER_LENGTH * 2;
  440. }
  441. iBegin = iSize;
  442. iSize += iLength;
  443. if(iSize + 1 < SUPER_LENGTH)
  444. {
  445. // buffer the message up
  446. if(blnBanner)
  447. {
  448. wcscat(strWriteBuffer, BANNER);
  449. }
  450. if(blnTime)
  451. {
  452. WCHAR strDate[LANGUAGE_LENGTH];
  453. WCHAR strTime[LANGUAGE_LENGTH];
  454. SYSTEMTIME SystemTime;
  455. GetLocalTime(&SystemTime);
  456. GetDateFormatW(LOCALE_USER_DEFAULT, 0, &SystemTime,
  457. L"MMddyy", strDate, LANGUAGE_LENGTH);
  458. GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &SystemTime,
  459. L"HHmmss", strTime, LANGUAGE_LENGTH);
  460. wcscat(strWriteBuffer, strDate);
  461. wcscat(strWriteBuffer, strTime);
  462. wcscat(strWriteBuffer, SPACE);
  463. }
  464. wcscat(strWriteBuffer, msg);
  465. wcscat(strWriteBuffer, ENDOFLINE);
  466. wcscat(strWriteBuffer, CRETURN);
  467. if(blnBanner)
  468. {
  469. wcscat(strWriteBuffer, BANNER);
  470. }
  471. strWriteBuffer[iSize] = 0;
  472. }
  473. else
  474. {
  475. // overflow
  476. WriteFile(g_hDebugFile, strWriteBuffer,
  477. iBegin * sizeof(WCHAR), &iWriteBytes, NULL);
  478. ZeroMemory(strWriteBuffer, SUPER_LENGTH * sizeof(WCHAR));
  479. if(blnBanner)
  480. {
  481. wcscat(strWriteBuffer, BANNER);
  482. }
  483. if(blnTime)
  484. {
  485. WCHAR strDate[LANGUAGE_LENGTH];
  486. WCHAR strTime[LANGUAGE_LENGTH];
  487. SYSTEMTIME SystemTime;
  488. GetLocalTime(&SystemTime);
  489. GetDateFormatW(LOCALE_USER_DEFAULT, 0,
  490. &SystemTime, L"MMddyy", strDate, LANGUAGE_LENGTH);
  491. GetTimeFormatW(LOCALE_USER_DEFAULT,
  492. 0, &SystemTime, L"HHmmss", strTime,
  493. LANGUAGE_LENGTH);
  494. wcscat(strWriteBuffer, strDate);
  495. wcscat(strWriteBuffer, strTime);
  496. wcscat(strWriteBuffer, SPACE);
  497. }
  498. wcscat(strWriteBuffer, msg);
  499. wcscat(strWriteBuffer, ENDOFLINE);
  500. wcscat(strWriteBuffer, CRETURN);
  501. if(blnBanner)
  502. {
  503. wcscat(strWriteBuffer, BANNER);
  504. }
  505. iSize = iLength;
  506. }
  507. }
  508. else if(blnFlush && iSize > 0)
  509. {
  510. // flush the buffer to file
  511. WriteFile(g_hDebugFile, strWriteBuffer, iSize * sizeof(WCHAR), &iWriteBytes, NULL);
  512. iSize = 0;
  513. }
  514. if(blnPrint)
  515. {
  516. wprintf(L"%ls\n", msg);
  517. }
  518. }
  519. else if(msg && blnPrint)
  520. {
  521. wprintf(L"%ls\n", msg);
  522. }
  523. }
  524. __finally
  525. {
  526. LeaveCriticalSection(&g_CSLogFile);
  527. }
  528. }
  529. ///////////////////////////////////////////////////////////////////////////////
  530. //
  531. // DisplayHelpMessage, shows what to do if the user if confused
  532. //
  533. // Parameters:
  534. //
  535. // none
  536. //
  537. // Return:
  538. //
  539. // none
  540. //
  541. ///////////////////////////////////////////////////////////////////////////////
  542. VOID DisplayHelpMessage(VOID)
  543. {
  544. printf("\nFor help try /?.\n");
  545. }
  546. ///////////////////////////////////////////////////////////////////////////////
  547. //
  548. // DisplayHelp, shows what oempatch is cabable of
  549. //
  550. // Parameters:
  551. //
  552. // none
  553. //
  554. // Return:
  555. //
  556. // none
  557. //
  558. ///////////////////////////////////////////////////////////////////////////////
  559. VOID DisplayHelp(VOID)
  560. {
  561. printf("\nOEMPatch %d.%.3d - Help\n", g_iMajorVersion, g_iMinorVersion);
  562. printf("Usage: oempatch [/m#] [/s] [/b] [/l | /L [C] ] [/g | /?]\n");
  563. printf("(no switch):\trun patch, single thread\n");
  564. printf("/m#:\t\trun multi-threaded patch, maximum # is 9, minimum is 1\n");
  565. printf("/s:\t\tcollect patching stats when running patch, slows down patching\n");
  566. printf("/b:\t\tchoose best patching methods, slows down patching\n");
  567. printf("/l:\t\trun patch with append to the log file OEMPatch.log\n");
  568. printf("/L:\t\trun patch with a new log file OEMPatch.log\n");
  569. printf("/lC, /LC:\tevery file status is logged\n");
  570. printf("/g:\t\tgenerate a new sample answer file if there is no such file\n");
  571. printf("/?:\t\tdisplay help\n");
  572. }
  573. ///////////////////////////////////////////////////////////////////////////////
  574. //
  575. // DisplayHelp, creates a sample answer file, so that the user can modifie
  576. // according to needs
  577. //
  578. // Parameters:
  579. //
  580. // none
  581. //
  582. // Return:
  583. //
  584. // none
  585. //
  586. ///////////////////////////////////////////////////////////////////////////////
  587. VOID GenerateAnswerFile(VOID)
  588. {
  589. HANDLE hAnsFile = INVALID_HANDLE_VALUE;
  590. ULONG iWriteByte = 0;
  591. ULONG i = 0;
  592. hAnsFile = CreateFileW(ANS_FILE_NAME,
  593. GENERIC_WRITE,
  594. 0,
  595. (LPSECURITY_ATTRIBUTES)NULL,
  596. CREATE_NEW,
  597. FILE_ATTRIBUTE_NORMAL,
  598. (HANDLE)NULL);
  599. if(hAnsFile != INVALID_HANDLE_VALUE)
  600. {
  601. // construct the answer file
  602. WriteFile(hAnsFile, &UNICODE_HEAD, sizeof(WCHAR), &iWriteByte, NULL);
  603. // SAMPLEFILE is defined in ansparse.h
  604. while(SAMPLEFILE[i][0] &&
  605. WriteFile(hAnsFile, SAMPLEFILE[i],
  606. wcslen(SAMPLEFILE[i]) * sizeof(WCHAR), &iWriteByte, NULL))
  607. {
  608. i++;
  609. }
  610. CloseHandle(hAnsFile);
  611. if(SAMPLEFILE[i][0])
  612. {
  613. printf("Warning:OEMPatch.ans may contain errors, needs regeneration.\n");
  614. }
  615. else
  616. {
  617. printf("OEMPatch.ans generated, needs to be manually completed and saved as UNICODE.\n");
  618. }
  619. }
  620. else
  621. {
  622. printf("OEMPatch.ans already exists, no new file created.\n");
  623. }
  624. }