PATCH.CPP 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /*
  2. ** Command & Conquer Red Alert(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. LPVOID CALLBACK __export 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. return;
  176. }
  177. DBGMSG("Patch SKU = "<<skuIndex);
  178. PATCHFUNC patchFunc;
  179. patchFunc=(PATCHFUNC)GetProcAddress(hInst,"RTPatchApply32@12");
  180. if (patchFunc==NULL)
  181. {
  182. char message[256];
  183. LoadString(NULL,IDS_BAD_LIBRARY,message,256);
  184. char title[128];
  185. LoadString(NULL,IDS_ERROR,title,128);
  186. MessageBox(NULL,message,title,MB_OK);
  187. return;
  188. }
  189. char patchArgs[200];
  190. sprintf(patchArgs,"\"%s\" .",patchfile);
  191. int rtpErrCode=patchFunc(patchArgs,PatchCallBack,TRUE);
  192. FreeLibrary(hInst); // unload the DLL
  193. _unlink(patchfile); // delete the patch
  194. DestroyWindow(dialog); // get rid of the dialog
  195. // Now we have to update the registry so the version is correct
  196. // The version is the first integer in the filename
  197. // Eg: 22456_patch.rtp means version 22456 goes into the registry
  198. // The version# starts after the last '\' char
  199. char *cptr=patchfile;
  200. char *tempPtr;
  201. DWORD version;
  202. while( (tempPtr=strchr(cptr,'\\')) !=NULL)
  203. cptr=tempPtr+1;
  204. if (cptr)
  205. version=atol(cptr);
  206. DBGMSG("VERSION TO = "<<version);
  207. char string[256];
  208. Wstring key;
  209. // Get the InstallPath from the specified registry key
  210. sprintf(string,"SKU%d",skuIndex);
  211. if (config.getString(string,key)==FALSE)
  212. {
  213. ERRMSGX("SKU is missing from config file!", "D:\\dev\\launcher\\patch.cpp"); //ERRMSG("SKU is missing from config file!");
  214. return;
  215. }
  216. int temp=0;
  217. Wstring sku;
  218. Wstring path;
  219. temp=key.getToken(temp," ",sku);
  220. path=key;
  221. path.remove(0,temp);
  222. while((*(path.get()))==' ') // remove leading spaces
  223. path.remove(0,1);
  224. // Open the registry key for modifying now...
  225. HKEY regKey;
  226. LONG regRetval;
  227. regRetval=RegOpenKeyEx(HKEY_LOCAL_MACHINE,path.get(),0,
  228. KEY_ALL_ACCESS,&regKey);
  229. if (regRetval!=ERROR_SUCCESS)
  230. DBGMSG("Can't open reg key for writing");
  231. regRetval=RegSetValueEx(regKey,"Version",0,REG_DWORD,(uint8 *)&version,
  232. sizeof(version));
  233. // Create blocking DLG for update info
  234. DialogBox(Global_instance,MAKEINTRESOURCE(IDD_CHANGELOG),NULL,(DLGPROC)Update_Info_Proc);
  235. }
  236. //
  237. // Execute now (without rebooting) type patch
  238. //
  239. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exn"),".exn")==0)
  240. {
  241. Process proc;
  242. strcpy(proc.directory,".");
  243. strcpy(proc.command,patchfile);
  244. Create_Process(proc);
  245. Wait_Process(proc);
  246. _unlink(patchfile);
  247. }
  248. //
  249. // Web link type patch
  250. //
  251. // Im about 99.44% sure that this is completely useless.
  252. //
  253. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".web"),".web")==0)
  254. {
  255. char message[256];
  256. LoadString(NULL,IDS_WEBPATCH,message,256);
  257. char title[128];
  258. LoadString(NULL,IDS_WEBPATCH_TITLE,title,128);
  259. MessageBox(NULL,message,title,MB_OK);
  260. FILE *in=fopen(patchfile,"r");
  261. if (in!=NULL)
  262. {
  263. char URL[256];
  264. fgets(URL,255,in);
  265. fclose(in);
  266. ShellExecute(NULL,NULL,URL,NULL,".",SW_SHOW);
  267. _unlink(patchfile);
  268. //// This is somewhat skanky, but we can't wait
  269. //// for the viewer to exit (I tried).
  270. exit(0);
  271. }
  272. else
  273. {
  274. MessageBox(NULL,patchfile,"Patchfile vanished?",MB_OK);
  275. }
  276. }
  277. }
  278. void Shutdown_Computer_Now(void)
  279. {
  280. HANDLE hToken;
  281. TOKEN_PRIVILEGES tkp;
  282. // Get a token for this process.
  283. if (!OpenProcessToken(GetCurrentProcess(),
  284. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
  285. {
  286. //error("OpenProcessToken");
  287. }
  288. // Get the LUID for the shutdown privilege.
  289. LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
  290. &tkp.Privileges[0].Luid);
  291. tkp.PrivilegeCount = 1; // one privilege to set
  292. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  293. // Get the shutdown privilege for this process.
  294. AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
  295. (PTOKEN_PRIVILEGES)NULL, 0);
  296. // Cannot test the return value of AdjustTokenPrivileges.
  297. if (GetLastError() != ERROR_SUCCESS)
  298. {
  299. //error("AdjustTokenPrivileges");
  300. }
  301. // Shut down the system and force all applications to close.
  302. if (!ExitWindowsEx(EWX_REBOOT, 0))
  303. {
  304. // Should never happen
  305. char restart[128];
  306. LoadString(NULL,IDS_MUST_RESTART,restart,128);
  307. MessageBox(NULL,restart,"OK",MB_OK);
  308. exit(0);
  309. }
  310. MSG msg;
  311. while (GetMessage(&msg, NULL, 0, 0))
  312. {
  313. TranslateMessage( &msg );
  314. DispatchMessage( &msg );
  315. }
  316. }
  317. //
  318. // Callback during the patching process
  319. //
  320. LPVOID CALLBACK __export PatchCallBack(UINT Id, LPVOID Param)
  321. {
  322. char string[128];
  323. static int fileCount=0; // number of files to be patched
  324. static int currFile=0;
  325. // Make sure our windows get updated
  326. MSG msg;
  327. int counter=0;
  328. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ))
  329. {
  330. TranslateMessage( &msg );
  331. DispatchMessage( &msg );
  332. counter++;
  333. if (counter==100) // just in case...
  334. break;
  335. }
  336. LPVOID RetVal="";
  337. bit8 Abort=FALSE;
  338. //// using the global Dialog pointer, set the current "error" code
  339. //g_DlgPtr->SetRTPErrCode(Id);
  340. int percent;
  341. switch( Id )
  342. {
  343. case 1:
  344. case 2:
  345. // Warning message header/text
  346. DBGMSG("P_MSG: "<<((char *)Param));
  347. break;
  348. case 3:
  349. // Error message header
  350. DBGMSG("P_MSG: "<<((char *)Param));
  351. break;
  352. case 4:
  353. // Error message header/text
  354. ///////*g_LogFile << (char *)Parm << endl;
  355. MessageBox(NULL,(char *)Param,"ERROR",MB_OK);
  356. {
  357. FILE *out=fopen("patch.err","a");
  358. time_t timet=time(NULL);
  359. fprintf(out,"\n\nPatch Erorr: %s\n",ctime(&timet));
  360. fprintf(out,"%s\n",(char *)Param);
  361. fclose(out);
  362. }
  363. break;
  364. case 9:
  365. // progress message
  366. break;
  367. case 0xa:
  368. // help message
  369. break;
  370. case 0xb:
  371. // patch file comment
  372. break;
  373. case 0xc:
  374. // copyright message
  375. break; // these just display text
  376. case 5:
  377. // % completed
  378. // so adjust the progress bar using the global Dialog pointer
  379. /////////g_DlgPtr->SetProgressBar((int)((float)(*(UINT *)Parm)/(float)0x8000*(float)100));
  380. percent=((*(UINT *)Param)*100)/0x8000;
  381. SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  382. break;
  383. case 6:
  384. // Number of patch files
  385. DBGMSG("6: "<<*((uint32 *)Param));
  386. fileCount=*((uint32 *)Param);
  387. currFile=0;
  388. break;
  389. case 7:
  390. //// begin patch
  391. //LoadString(g_AppInstance, IDS_PROCESSING, lpcBuf, 256);
  392. //strcpy(buf,lpcBuf);
  393. //strcat(buf,(char *)Parm);
  394. //g_DlgPtr->SetProgressText(buf);
  395. //*g_LogFile << buf << " : ";
  396. //fileModified = true;
  397. DBGMSG("7: "<<(char *)Param);
  398. SetWindowText(GetDlgItem(PatchDialog,IDC_FILENAME),(char *)Param);
  399. percent=0;
  400. SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  401. currFile++;
  402. char xofy[64];
  403. LoadString(NULL,IDS_FILE_X_OF_Y,xofy,64);
  404. sprintf(string,xofy,currFile,fileCount);
  405. SetWindowText(GetDlgItem(PatchDialog,IDC_CAPTION),string);
  406. break;
  407. case 8:
  408. //// end patch
  409. //LoadString(g_AppInstance, IDS_PROCCOMPLETE, lpcBuf, 256);
  410. //g_DlgPtr->SetProgressText(lpcBuf);
  411. //*g_LogFile << " complete" << endl;
  412. percent=100;
  413. SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  414. DBGMSG("P_DONE");
  415. break;
  416. case 0xd:
  417. //// this one shouldn't happen (only occurs if the command line
  418. //// doesn't have a patch file in it, and we insure that it does).
  419. //Abort = TRUE;
  420. //*g_LogFile << "Incorrect (or none) patch file specified in command line." << endl;
  421. break;
  422. case 0xe:
  423. //// this one shouldn't happen either (same reason)
  424. //Abort = TRUE;
  425. //*g_LogFile << "Incorrect (or none) path specified in command line." << endl;
  426. break;
  427. case 0xf:
  428. //// Password Dialog
  429. break;
  430. case 0x10:
  431. //// Invalid Password Alert
  432. break;
  433. case 0x11:
  434. //// Disk Change Dialog
  435. break;
  436. case 0x12:
  437. //// Disk Change Alert
  438. break;
  439. case 0x13:
  440. //// Confirmation Dialog
  441. break;
  442. case 0x14:
  443. //// Location Dialog
  444. //Abort = TRUE;
  445. //*g_LogFile << "Specified path is incorrect." << endl;
  446. break;
  447. case 0x16:
  448. //// Searching Call-back
  449. break;
  450. case 0x15:
  451. //// Idle...
  452. break;
  453. default:
  454. break;
  455. }
  456. if(Abort)
  457. return (NULL);
  458. else
  459. return (RetVal);
  460. }