patch.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*
  2. ** Command & Conquer Renegade(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. int rtpErrCode = -1;
  105. DWORD CALLBACK ApplyPatchThread(LPVOID _file) {
  106. char *patchfile = (char *)_file;
  107. HINSTANCE hInst=LoadLibrary("patchw32.dll");
  108. if (hInst==NULL)
  109. {
  110. char message[256];
  111. LoadString(NULL,IDS_ERR_MISSING_FILE,message,256);
  112. char string[256];
  113. sprintf(string,message,"patchw32.dll");
  114. char title[128];
  115. LoadString(NULL,IDS_ERROR,title,128);
  116. MessageBox(NULL,string,title,MB_OK);
  117. rtpErrCode = -2;
  118. return -1;
  119. }
  120. PATCHFUNC patchFunc;
  121. patchFunc=(PATCHFUNC)GetProcAddress(hInst,"RTPatchApply32@12");
  122. if (patchFunc==NULL)
  123. {
  124. char message[256];
  125. LoadString(NULL,IDS_BAD_LIBRARY,message,256);
  126. char title[128];
  127. LoadString(NULL,IDS_ERROR,title,128);
  128. MessageBox(NULL,message,title,MB_OK);
  129. rtpErrCode = -2;
  130. FreeLibrary(hInst); // unload the DLL
  131. return -1;
  132. }
  133. char patchArgs[200];
  134. sprintf(patchArgs,"\"%s\" .",patchfile);
  135. rtpErrCode = patchFunc(patchArgs,PatchCallBack,TRUE);
  136. FreeLibrary(hInst); // unload the DLL
  137. return 0;
  138. }
  139. //
  140. // Apply any type of patch. Filename in patchfile. Product base registry
  141. // (eg: "SOFTWARE\Westwood\Red Alert") should be in the config file as
  142. // SKUX SKU base reg dir where X = index
  143. //
  144. void Apply_Patch(char *patchfile,ConfigFile &config,int skuIndex, bool show_dialog)
  145. {
  146. DBGMSG("PATCHFILE : "<<patchfile);
  147. char cwdbuf[256];
  148. _getcwd(cwdbuf,255);
  149. DBGMSG("CWD : "<<cwdbuf);
  150. //
  151. // If patch is a .exe type patch
  152. //
  153. if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exe"),".exe")==0)
  154. {
  155. // Set this as a run once service thing
  156. HKEY regKey;
  157. LONG regRetval;
  158. DWORD regPrevious;
  159. regRetval=RegCreateKeyEx(
  160. HKEY_LOCAL_MACHINE,
  161. "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
  162. 0,
  163. "",
  164. REG_OPTION_NON_VOLATILE,
  165. KEY_ALL_ACCESS,
  166. NULL,
  167. &regKey,
  168. &regPrevious);
  169. if (regRetval==ERROR_SUCCESS)
  170. {
  171. RegSetValueEx(regKey,"EXEPatch",0,REG_SZ,(const uint8*)patchfile,strlen(patchfile)+1);
  172. char message[256];
  173. LoadString(NULL,IDS_SYS_RESTART,message,256);
  174. char title[128];
  175. LoadString(NULL,IDS_SYS_RESTART_TITLE,title,128);
  176. MessageBox(NULL,message,title,MB_OK);
  177. Shutdown_Computer_Now();
  178. }
  179. else
  180. {
  181. char message[256];
  182. LoadString(NULL,IDS_RUNONCE_ERR,message,256);
  183. char string[256];
  184. sprintf(string,message,patchfile);
  185. MessageBox(NULL,string,"ERROR",MB_OK);
  186. }
  187. }
  188. //
  189. // RTPatch type patch
  190. //
  191. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".rtp"),".rtp")==0)
  192. {
  193. MSG msg;
  194. HWND dialog=Create_Patch_Dialog();
  195. while(PeekMessage(&msg,NULL,0,0, PM_REMOVE))
  196. {
  197. TranslateMessage(&msg);
  198. DispatchMessage(&msg);
  199. }
  200. DBGMSG("Patch SKU = "<<skuIndex);
  201. DWORD patchID = 0;
  202. HANDLE patchThread = CreateThread(NULL, 0, ApplyPatchThread, (LPVOID)patchfile, 0, &patchID);
  203. int dotcnt = 9;
  204. DWORD last_time = GetTickCount();
  205. BOOL forward = FALSE;
  206. char worktext[256];
  207. LoadString(NULL,IDS_WORKING_TEXT,worktext,sizeof(worktext)-12);
  208. int scnt = strlen(worktext)+1;
  209. strcat(worktext, " ..........");
  210. // Wait for the thread to finish
  211. while (rtpErrCode == -1) {
  212. if (GetTickCount() - last_time > 100) {
  213. if (forward) {
  214. worktext[scnt+dotcnt] = '.';
  215. if (dotcnt == 9) forward = FALSE;
  216. else dotcnt++;
  217. } else {
  218. worktext[scnt+dotcnt] = 0;
  219. if (dotcnt == 0) forward = TRUE;
  220. else dotcnt--;
  221. }
  222. SetWindowText(GetDlgItem(PatchDialog,IDC_WORKING_BOX),(char *)worktext);
  223. last_time = GetTickCount();
  224. }
  225. while(PeekMessage(&msg,NULL,0,0, PM_REMOVE))
  226. {
  227. TranslateMessage(&msg);
  228. DispatchMessage(&msg);
  229. }
  230. Sleep(5);
  231. }
  232. CloseHandle(patchThread);
  233. _unlink(patchfile); // delete the patch
  234. DestroyWindow(dialog); // get rid of the dialog
  235. // Don't update the registry if the patch failed to update.
  236. if (rtpErrCode != 0)
  237. {
  238. ERRMSG("Patch error: " << rtpErrCode);
  239. return;
  240. }
  241. // Now we have to update the registry so the version is correct
  242. // The version is the first integer in the filename
  243. // Eg: 22456_patch.rtp means version 22456 goes into the registry
  244. // The version# starts after the last '\' char
  245. char *cptr=patchfile;
  246. char *tempPtr;
  247. DWORD version;
  248. while( (tempPtr=strchr(cptr,'\\')) !=NULL)
  249. cptr=tempPtr+1;
  250. if (cptr)
  251. version=atol(cptr);
  252. DBGMSG("VERSION TO = "<<version);
  253. char string[256];
  254. Wstring key;
  255. // Get the InstallPath from the specified registry key
  256. sprintf(string,"SKU%d",skuIndex);
  257. if (config.getString(string,key)==FALSE)
  258. {
  259. ERRMSG("SKU is missing from config file!");
  260. return;
  261. }
  262. int temp=0;
  263. Wstring sku;
  264. Wstring path;
  265. temp=key.getToken(temp," ",sku);
  266. path=key;
  267. path.remove(0,temp);
  268. while((*(path.get()))==' ') // remove leading spaces
  269. path.remove(0,1);
  270. // Open the registry key for modifying now...
  271. HKEY regKey;
  272. LONG regRetval;
  273. regRetval=RegOpenKeyEx(HKEY_LOCAL_MACHINE,path.get(),0,
  274. KEY_ALL_ACCESS,&regKey);
  275. if (regRetval!=ERROR_SUCCESS)
  276. DBGMSG("Can't open reg key for writing");
  277. regRetval=RegSetValueEx(regKey,"Version",0,REG_DWORD,(uint8 *)&version,
  278. sizeof(version));
  279. // Create blocking DLG for update info
  280. if (show_dialog) {
  281. DialogBox(Global_instance,MAKEINTRESOURCE(IDD_CHANGELOG),NULL,(DLGPROC)Update_Info_Proc);
  282. }
  283. }
  284. //
  285. // Execute now (without rebooting) type patch
  286. //
  287. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exn"),".exn")==0)
  288. {
  289. Process proc;
  290. strcpy(proc.directory,".");
  291. strcpy(proc.command,patchfile);
  292. Create_Process(proc);
  293. Wait_Process(proc);
  294. _unlink(patchfile);
  295. }
  296. //
  297. // Web link type patch
  298. //
  299. // Im about 99.44% sure that this is completely useless.
  300. //
  301. else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".web"),".web")==0)
  302. {
  303. char message[256];
  304. LoadString(NULL,IDS_WEBPATCH,message,256);
  305. char title[128];
  306. LoadString(NULL,IDS_WEBPATCH_TITLE,title,128);
  307. MessageBox(NULL,message,title,MB_OK);
  308. FILE *in=fopen(patchfile,"r");
  309. if (in!=NULL)
  310. {
  311. char URL[256];
  312. fgets(URL,255,in);
  313. fclose(in);
  314. ShellExecute(NULL,NULL,URL,NULL,".",SW_SHOW);
  315. _unlink(patchfile);
  316. //// This is somewhat skanky, but we can't wait
  317. //// for the viewer to exit (I tried).
  318. exit(0);
  319. }
  320. else
  321. {
  322. MessageBox(NULL,patchfile,"Patchfile vanished?",MB_OK);
  323. }
  324. }
  325. }
  326. void Shutdown_Computer_Now(void)
  327. {
  328. HANDLE hToken;
  329. TOKEN_PRIVILEGES tkp;
  330. // Get a token for this process.
  331. if (!OpenProcessToken(GetCurrentProcess(),
  332. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
  333. {
  334. //error("OpenProcessToken");
  335. }
  336. // Get the LUID for the shutdown privilege.
  337. LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
  338. &tkp.Privileges[0].Luid);
  339. tkp.PrivilegeCount = 1; // one privilege to set
  340. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  341. // Get the shutdown privilege for this process.
  342. AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
  343. (PTOKEN_PRIVILEGES)NULL, 0);
  344. // Cannot test the return value of AdjustTokenPrivileges.
  345. if (GetLastError() != ERROR_SUCCESS)
  346. {
  347. //error("AdjustTokenPrivileges");
  348. }
  349. // Shut down the system and force all applications to close.
  350. if (!ExitWindowsEx(EWX_REBOOT, 0))
  351. {
  352. // Should never happen
  353. char restart[128];
  354. LoadString(NULL,IDS_MUST_RESTART,restart,128);
  355. MessageBox(NULL,restart,"OK",MB_OK);
  356. exit(0);
  357. }
  358. MSG msg;
  359. while (GetMessage(&msg, NULL, 0, 0))
  360. {
  361. TranslateMessage( &msg );
  362. DispatchMessage( &msg );
  363. }
  364. }
  365. //
  366. // Callback during the patching process
  367. //
  368. __declspec(dllexport) LPVOID CALLBACK PatchCallBack(UINT Id, LPVOID Param)
  369. {
  370. char string[128];
  371. static int fileCount=0; // number of files to be patched
  372. static int currFile=0;
  373. // Make sure our windows get updated
  374. // MSG msg;
  375. int counter=0;
  376. // Let the main thread do this now.
  377. // while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ))
  378. // {
  379. // TranslateMessage( &msg );
  380. // DispatchMessage( &msg );
  381. // counter++;
  382. // if (counter==100) // just in case...
  383. // break;
  384. // }
  385. LPVOID RetVal="";
  386. bit8 Abort=FALSE;
  387. //// using the global Dialog pointer, set the current "error" code
  388. //g_DlgPtr->SetRTPErrCode(Id);
  389. int percent;
  390. switch( Id )
  391. {
  392. case 1:
  393. case 2:
  394. // Warning message header/text
  395. DBGMSG("P_MSG: "<<((char *)Param));
  396. break;
  397. case 3:
  398. // Error message header
  399. DBGMSG("P_MSG: "<<((char *)Param));
  400. break;
  401. case 4:
  402. // Error message header/text
  403. ///////*g_LogFile << (char *)Parm << endl;
  404. char errmsg[256];
  405. LoadString(NULL,IDS_ERR_PATCH,errmsg,256);
  406. MessageBox(NULL,(char *)Param,errmsg,MB_OK);
  407. {
  408. FILE *out=fopen("patch.err","a");
  409. time_t timet=time(NULL);
  410. fprintf(out,"\n\nPatch Error: %s\n",ctime(&timet));
  411. fprintf(out,"%s\n",(char *)Param);
  412. fclose(out);
  413. }
  414. break;
  415. case 9:
  416. // progress message
  417. break;
  418. case 0xa:
  419. // help message
  420. break;
  421. case 0xb:
  422. // patch file comment
  423. break;
  424. case 0xc:
  425. // copyright message
  426. break; // these just display text
  427. case 5:
  428. // % completed
  429. // so adjust the progress bar using the global Dialog pointer
  430. /////////g_DlgPtr->SetProgressBar((int)((float)(*(UINT *)Parm)/(float)0x8000*(float)100));
  431. percent=((*(UINT *)Param)*100)/0x8000;
  432. PostMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  433. break;
  434. case 6:
  435. // Number of patch files
  436. DBGMSG("6: "<<*((uint32 *)Param));
  437. fileCount=*((uint32 *)Param);
  438. currFile=0;
  439. break;
  440. case 7:
  441. //// begin patch
  442. //LoadString(g_AppInstance, IDS_PROCESSING, lpcBuf, 256);
  443. //strcpy(buf,lpcBuf);
  444. //strcat(buf,(char *)Parm);
  445. //g_DlgPtr->SetProgressText(buf);
  446. //*g_LogFile << buf << " : ";
  447. //fileModified = true;
  448. DBGMSG("7: "<<(char *)Param);
  449. // PostMessage(GetDlgItem(PatchDialog,IDC_FILENAME),WM_SETTEXT,0,(LPARAM)Param);
  450. SetWindowText(GetDlgItem(PatchDialog,IDC_FILENAME),(char *)Param);
  451. percent=0;
  452. PostMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  453. currFile++;
  454. char xofy[64];
  455. LoadString(NULL,IDS_FILE_X_OF_Y,xofy,64);
  456. sprintf(string,xofy,currFile,fileCount);
  457. SetWindowText(GetDlgItem(PatchDialog,IDC_CAPTION),string);
  458. // PostMessage(GetDlgItem(PatchDialog,IDC_CAPTION),WM_SETTEXT,0,(LPARAM)string);
  459. break;
  460. case 8:
  461. //// end patch
  462. //LoadString(g_AppInstance, IDS_PROCCOMPLETE, lpcBuf, 256);
  463. //g_DlgPtr->SetProgressText(lpcBuf);
  464. //*g_LogFile << " complete" << endl;
  465. percent=100;
  466. PostMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
  467. DBGMSG("P_DONE");
  468. break;
  469. case 0xd:
  470. //// this one shouldn't happen (only occurs if the command line
  471. //// doesn't have a patch file in it, and we insure that it does).
  472. //Abort = TRUE;
  473. //*g_LogFile << "Incorrect (or none) patch file specified in command line." << endl;
  474. break;
  475. case 0xe:
  476. //// this one shouldn't happen either (same reason)
  477. //Abort = TRUE;
  478. //*g_LogFile << "Incorrect (or none) path specified in command line." << endl;
  479. break;
  480. case 0xf:
  481. //// Password Dialog
  482. break;
  483. case 0x10:
  484. //// Invalid Password Alert
  485. break;
  486. case 0x11:
  487. //// Disk Change Dialog
  488. break;
  489. case 0x12:
  490. //// Disk Change Alert
  491. break;
  492. case 0x13:
  493. //// Confirmation Dialog
  494. break;
  495. case 0x14:
  496. //// Location Dialog
  497. //Abort = TRUE;
  498. //*g_LogFile << "Specified path is incorrect." << endl;
  499. break;
  500. case 0x16:
  501. //// Searching Call-back
  502. break;
  503. case 0x15:
  504. //// Idle...
  505. break;
  506. default:
  507. break;
  508. }
  509. if(Abort)
  510. return (NULL);
  511. else
  512. return (RetVal);
  513. }