patch.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*****************************************************************************\
  19. C O N F I D E N T I A L --- W E S T W O O D S T U D I O S
  20. *******************************************************************************
  21. File: patch.cpp
  22. Programmer: Neal Kettler
  23. StartDate: Feb 6, 1998
  24. LastUpdate: Feb 10, 1998
  25. -------------------------------------------------------------------------------
  26. This is where all the code is for applying various types of patches.
  27. \*****************************************************************************/
  28. #include "patch.h"
  29. #include <shellapi.h>
  30. #include <direct.h>
  31. //
  32. // For the text box showing patch info
  33. //
  34. BOOL CALLBACK Update_Info_Proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  35. {
  36. static int unselectText=0;
  37. switch(iMsg)
  38. {
  39. case WM_INITDIALOG:
  40. {
  41. FILE *in = fopen("launcher.txt","r");
  42. if (in==NULL)
  43. {
  44. EndDialog(hwnd,-1);
  45. return(1);
  46. }
  47. char line[270];
  48. int lastsel=0;
  49. char *cptr=NULL;
  50. while(fgets(line,255,in))
  51. {
  52. //Get rid of any trailing junk
  53. while(1)
  54. {
  55. if (strlen(line)<1)
  56. break;
  57. cptr=line+(strlen(line))-1;
  58. if ((*cptr=='\r')||(*cptr=='\n'))
  59. *cptr=0;
  60. else
  61. break;
  62. }
  63. // ...and add back the gunk that windows likes
  64. strcat(line,"\r\r\n");
  65. SendDlgItemMessage(hwnd, IDC_TEXT, EM_SETSEL, (WPARAM)lastsel, (LPARAM)lastsel );
  66. SendDlgItemMessage(hwnd, IDC_TEXT, EM_REPLACESEL, 0, (LPARAM)(line) );
  67. SendDlgItemMessage(hwnd, IDC_TEXT, EM_GETSEL, (WPARAM)NULL, (LPARAM)&lastsel );
  68. }
  69. unselectText=1;
  70. fclose(in);
  71. return(1); // 1 means windows handles focus issues
  72. }
  73. break;
  74. case WM_PAINT:
  75. if (unselectText)
  76. SendDlgItemMessage(hwnd, IDC_TEXT, EM_SETSEL, -1, 0);
  77. unselectText=0;
  78. return(0);
  79. break;
  80. case WM_COMMAND:
  81. switch(wParam) {
  82. case IDOK:
  83. {
  84. EndDialog(hwnd,0);
  85. return(1);
  86. }
  87. default:
  88. break;
  89. }
  90. default:
  91. break;
  92. case WM_CLOSE:
  93. EndDialog(hwnd,0);
  94. return(1);
  95. break;
  96. }
  97. return(FALSE);
  98. }
  99. // Restart the computer for certain types of patches
  100. void Shutdown_Computer_Now(void);
  101. __declspec(dllexport) LPVOID CALLBACK PatchCallBack(UINT ID, LPVOID Param);
  102. typedef LPVOID (CALLBACK* PATCHCALLBACK)(UINT, LPVOID);
  103. typedef UINT (CALLBACK *PATCHFUNC)( LPSTR, PATCHCALLBACK, BOOL);
  104. //
  105. // Apply any type of patch. Filename in patchfile. Product base registry
  106. // (eg: "SOFTWARE\Westwood\Red Alert") should be in the config file as
  107. // SKUX SKU base reg dir where X = index
  108. //
  109. void Apply_Patch(char *patchfile,ConfigFile &config,int skuIndex)
  110. {
  111. DBGMSG("PATCHFILE : "<<patchfile);
  112. char cwdbuf[256];
  113. _getcwd(cwdbuf,255);
  114. DBGMSG("CWD : "<<cwdbuf);
  115. //
  116. // If patch is a .exe type patch
  117. //
  118. if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exe"),".exe")==0)
  119. {
  120. // Set this as a run once service thing
  121. HKEY regKey;
  122. LONG regRetval;
  123. DWORD regPrevious;
  124. regRetval=RegCreateKeyEx(
  125. HKEY_LOCAL_MACHINE,
  126. "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
  127. 0,
  128. "",
  129. REG_OPTION_NON_VOLATILE,
  130. KEY_ALL_ACCESS,
  131. NULL,
  132. &regKey,
  133. &regPrevious);
  134. if (regRetval==ERROR_SUCCESS)
  135. {
  136. RegSetValueEx(regKey,"EXEPatch",0,REG_SZ,(const uint8*)patchfile,strlen(patchfile)+1);
  137. char message[256];
  138. LoadString(NULL,IDS_SYS_RESTART,message,256);
  139. char title[128];
  140. LoadString(NULL,IDS_SYS_RESTART_TITLE,title,128);
  141. MessageBox(NULL,message,title,MB_OK);
  142. Shutdown_Computer_Now();
  143. }
  144. else
  145. {
  146. char message[256];
  147. LoadString(NULL,IDS_RUNONCE_ERR,message,256);
  148. char string[256];
  149. sprintf(string,message,patchfile);
  150. MessageBox(NULL,string,"ERROR",MB_OK);
  151. }
  152. }
  153. //
  154. // RTPatch type patch
  155. //
  156. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".rtp"),".rtp")==0)
  157. {
  158. MSG msg;
  159. HWND dialog=Create_Patch_Dialog();
  160. while(PeekMessage(&msg,NULL,0,0, PM_REMOVE))
  161. {
  162. TranslateMessage(&msg);
  163. DispatchMessage(&msg);
  164. }
  165. HINSTANCE hInst=LoadLibrary("patchw32.dll");
  166. if (hInst==NULL)
  167. {
  168. char message[256];
  169. LoadString(NULL,IDS_ERR_MISSING_FILE,message,256);
  170. char string[256];
  171. sprintf(string,message,"patchw32.dll");
  172. char title[128];
  173. LoadString(NULL,IDS_ERROR,title,128);
  174. MessageBox(NULL,string,title,MB_OK);
  175. exit(-1);
  176. return;
  177. }
  178. DBGMSG("Patch SKU = "<<skuIndex);
  179. PATCHFUNC patchFunc;
  180. patchFunc=(PATCHFUNC)GetProcAddress(hInst,"RTPatchApply32@12");
  181. if (patchFunc==NULL)
  182. {
  183. char message[256];
  184. LoadString(NULL,IDS_BAD_LIBRARY,message,256);
  185. char title[128];
  186. LoadString(NULL,IDS_ERROR,title,128);
  187. MessageBox(NULL,message,title,MB_OK);
  188. return;
  189. }
  190. char patchArgs[200];
  191. sprintf(patchArgs,"\"%s\" .",patchfile);
  192. int rtpErrCode=patchFunc(patchArgs,PatchCallBack,TRUE);
  193. FreeLibrary(hInst); // unload the DLL
  194. _unlink(patchfile); // delete the patch
  195. DestroyWindow(dialog); // get rid of the dialog
  196. // Don't update the registry if the patch failed to update.
  197. if (rtpErrCode != 0)
  198. {
  199. ERRMSG("Patch error: " << rtpErrCode);
  200. return;
  201. }
  202. // Now we have to update the registry so the version is correct
  203. // The version is the first integer in the filename
  204. // Eg: 22456_patch.rtp means version 22456 goes into the registry
  205. // The version# starts after the last '\' char
  206. char *cptr=patchfile;
  207. char *tempPtr;
  208. DWORD version;
  209. while( (tempPtr=strchr(cptr,'\\')) !=NULL)
  210. cptr=tempPtr+1;
  211. if (cptr)
  212. version=atol(cptr);
  213. DBGMSG("VERSION TO = "<<version);
  214. char string[256];
  215. Wstring key;
  216. // Get the InstallPath from the specified registry key
  217. sprintf(string,"SKU%d",skuIndex);
  218. if (config.getString(string,key)==FALSE)
  219. {
  220. ERRMSG("SKU is missing from config file!");
  221. return;
  222. }
  223. int temp=0;
  224. Wstring sku;
  225. Wstring path;
  226. temp=key.getToken(temp," ",sku);
  227. path=key;
  228. path.remove(0,temp);
  229. while((*(path.get()))==' ') // remove leading spaces
  230. path.remove(0,1);
  231. // Open the registry key for modifying now...
  232. HKEY regKey;
  233. LONG regRetval;
  234. regRetval=RegOpenKeyEx(HKEY_LOCAL_MACHINE,path.get(),0,
  235. KEY_ALL_ACCESS,&regKey);
  236. if (regRetval!=ERROR_SUCCESS)
  237. DBGMSG("Can't open reg key for writing");
  238. regRetval=RegSetValueEx(regKey,"Version",0,REG_DWORD,(uint8 *)&version,
  239. sizeof(version));
  240. // Create blocking DLG for update info
  241. #ifdef USE_NOTEPAD
  242. {
  243. Process notepad;
  244. // same dir as game
  245. strcpy(notepad.directory, ".");
  246. // the program that grabs game patches
  247. strcpy(notepad.command, "notepad");
  248. strcpy(notepad.args, " launcher.txt");
  249. Create_Process(notepad);
  250. Wait_Process(notepad);
  251. }
  252. #else
  253. DialogBox(Global_instance,MAKEINTRESOURCE(IDD_CHANGELOG),NULL,(DLGPROC)Update_Info_Proc);
  254. #endif
  255. }
  256. //
  257. // Execute now (without rebooting) type patch
  258. //
  259. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exn"),".exn")==0)
  260. {
  261. Process proc;
  262. strcpy(proc.directory,".");
  263. strcpy(proc.command,patchfile);
  264. Create_Process(proc);
  265. Wait_Process(proc);
  266. _unlink(patchfile);
  267. }
  268. //
  269. // Web link type patch
  270. //
  271. // Im about 99.44% sure that this is completely useless.
  272. //
  273. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".web"),".web")==0)
  274. {
  275. char message[256];
  276. LoadString(NULL,IDS_WEBPATCH,message,256);
  277. char title[128];
  278. LoadString(NULL,IDS_WEBPATCH_TITLE,title,128);
  279. MessageBox(NULL,message,title,MB_OK);
  280. FILE *in=fopen(patchfile,"r");
  281. if (in!=NULL)
  282. {
  283. char URL[256];
  284. fgets(URL,255,in);
  285. fclose(in);
  286. ShellExecute(NULL,NULL,URL,NULL,".",SW_SHOW);
  287. _unlink(patchfile);
  288. //// This is somewhat skanky, but we can't wait
  289. //// for the viewer to exit (I tried).
  290. exit(0);
  291. }
  292. else
  293. {
  294. MessageBox(NULL,patchfile,"Patchfile vanished?",MB_OK);
  295. }
  296. }
  297. }
  298. void Shutdown_Computer_Now(void)
  299. {
  300. HANDLE hToken;
  301. TOKEN_PRIVILEGES tkp;
  302. // Get a token for this process.
  303. if (!OpenProcessToken(GetCurrentProcess(),
  304. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
  305. {
  306. //error("OpenProcessToken");
  307. }
  308. // Get the LUID for the shutdown privilege.
  309. LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
  310. &tkp.Privileges[0].Luid);
  311. tkp.PrivilegeCount = 1; // one privilege to set
  312. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  313. // Get the shutdown privilege for this process.
  314. AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
  315. (PTOKEN_PRIVILEGES)NULL, 0);
  316. // Cannot test the return value of AdjustTokenPrivileges.
  317. if (GetLastError() != ERROR_SUCCESS)
  318. {
  319. //error("AdjustTokenPrivileges");
  320. }
  321. // Shut down the system and force all applications to close.
  322. if (!ExitWindowsEx(EWX_REBOOT, 0))
  323. {
  324. // Should never happen
  325. char restart[128];
  326. LoadString(NULL,IDS_MUST_RESTART,restart,128);
  327. MessageBox(NULL,restart,"OK",MB_OK);
  328. exit(0);
  329. }
  330. MSG msg;
  331. while (GetMessage(&msg, NULL, 0, 0))
  332. {
  333. TranslateMessage( &msg );
  334. DispatchMessage( &msg );
  335. }
  336. }
  337. //
  338. // Callback during the patching process
  339. //
  340. __declspec(dllexport) LPVOID CALLBACK PatchCallBack(UINT Id, LPVOID Param)
  341. {
  342. char string[128];
  343. static int fileCount=0; // number of files to be patched
  344. static int currFile=0;
  345. // Make sure our windows get updated
  346. MSG msg;
  347. int counter=0;
  348. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ))
  349. {
  350. TranslateMessage( &msg );
  351. DispatchMessage( &msg );
  352. counter++;
  353. if (counter==100) // just in case...
  354. break;
  355. }
  356. LPVOID RetVal="";
  357. bit8 Abort=FALSE;
  358. //// using the global Dialog pointer, set the current "error" code
  359. //g_DlgPtr->SetRTPErrCode(Id);
  360. int percent;
  361. switch( Id )
  362. {
  363. case 1:
  364. case 2:
  365. // Warning message header/text
  366. DBGMSG("P_MSG: "<<((char *)Param));
  367. break;
  368. case 3:
  369. // Error message header
  370. DBGMSG("P_MSG: "<<((char *)Param));
  371. break;
  372. case 4:
  373. // Error message header/text
  374. ///////*g_LogFile << (char *)Parm << endl;
  375. char errmsg[256];
  376. LoadString(NULL,IDS_ERR_PATCH,errmsg,256);
  377. MessageBox(NULL,(char *)Param,errmsg,MB_OK);
  378. {
  379. FILE *out=fopen("patch.err","a");
  380. time_t timet=time(NULL);
  381. fprintf(out,"\n\nPatch Error: %s\n",ctime(&timet));
  382. fprintf(out,"%s\n",(char *)Param);
  383. fclose(out);
  384. }
  385. break;
  386. case 9:
  387. // progress message
  388. break;
  389. case 0xa:
  390. // help message
  391. break;
  392. case 0xb:
  393. // patch file comment
  394. break;
  395. case 0xc:
  396. // copyright message
  397. // Need to return this so Foreign Lang chars don't mess up.
  398. return "ANSI";
  399. case 5:
  400. // % completed
  401. // so adjust the progress bar using the global Dialog pointer
  402. /////////g_DlgPtr->SetProgressBar((int)((float)(*(UINT *)Parm)/(float)0x8000*(float)100));
  403. percent=((*(UINT *)Param)*100)/0x8000;
  404. SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  405. break;
  406. case 6:
  407. // Number of patch files
  408. DBGMSG("6: "<<*((uint32 *)Param));
  409. fileCount=*((uint32 *)Param);
  410. currFile=0;
  411. break;
  412. case 7:
  413. //// begin patch
  414. //LoadString(g_AppInstance, IDS_PROCESSING, lpcBuf, 256);
  415. //strcpy(buf,lpcBuf);
  416. //strcat(buf,(char *)Parm);
  417. //g_DlgPtr->SetProgressText(buf);
  418. //*g_LogFile << buf << " : ";
  419. //fileModified = true;
  420. DBGMSG("7: "<<(char *)Param);
  421. SetWindowText(GetDlgItem(PatchDialog,IDC_FILENAME),(char *)Param);
  422. percent=0;
  423. SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  424. currFile++;
  425. char xofy[64];
  426. LoadString(NULL,IDS_FILE_X_OF_Y,xofy,64);
  427. sprintf(string,xofy,currFile,fileCount);
  428. SetWindowText(GetDlgItem(PatchDialog,IDC_CAPTION),string);
  429. break;
  430. case 8:
  431. //// end patch
  432. //LoadString(g_AppInstance, IDS_PROCCOMPLETE, lpcBuf, 256);
  433. //g_DlgPtr->SetProgressText(lpcBuf);
  434. //*g_LogFile << " complete" << endl;
  435. percent=100;
  436. SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  437. DBGMSG("P_DONE");
  438. break;
  439. case 0xd:
  440. //// this one shouldn't happen (only occurs if the command line
  441. //// doesn't have a patch file in it, and we insure that it does).
  442. //Abort = TRUE;
  443. //*g_LogFile << "Incorrect (or none) patch file specified in command line." << endl;
  444. break;
  445. case 0xe:
  446. //// this one shouldn't happen either (same reason)
  447. //Abort = TRUE;
  448. //*g_LogFile << "Incorrect (or none) path specified in command line." << endl;
  449. break;
  450. case 0xf:
  451. //// Password Dialog
  452. break;
  453. case 0x10:
  454. //// Invalid Password Alert
  455. break;
  456. case 0x11:
  457. //// Disk Change Dialog
  458. break;
  459. case 0x12:
  460. //// Disk Change Alert
  461. break;
  462. case 0x13:
  463. //// Confirmation Dialog
  464. break;
  465. case 0x14:
  466. //// Location Dialog
  467. //Abort = TRUE;
  468. //*g_LogFile << "Specified path is incorrect." << endl;
  469. break;
  470. case 0x16:
  471. //// Searching Call-back
  472. break;
  473. case 0x15:
  474. //// Idle...
  475. break;
  476. default:
  477. break;
  478. }
  479. if(Abort)
  480. return (NULL);
  481. else
  482. return (RetVal);
  483. }