| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /*
- ** Command & Conquer Red Alert(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /*****************************************************************************\
- 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
- *******************************************************************************
- File: patch.cpp
- Programmer: Neal Kettler
-
- StartDate: Feb 6, 1998
- LastUpdate: Feb 10, 1998
- -------------------------------------------------------------------------------
- This is where all the code is for applying various types of patches.
- \*****************************************************************************/
- #include "patch.h"
- #include <shellapi.h>
- #include <direct.h>
- //
- // For the text box showing patch info
- //
- BOOL CALLBACK Update_Info_Proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
- {
- static int unselectText=0;
- switch(iMsg)
- {
- case WM_INITDIALOG:
- {
- FILE *in = fopen("launcher.txt","r");
- if (in==NULL)
- {
- EndDialog(hwnd,-1);
- return(1);
- }
- char line[270];
- int lastsel=0;
- char *cptr=NULL;
- while(fgets(line,255,in))
- {
- //Get rid of any trailing junk
- while(1)
- {
- if (strlen(line)<1)
- break;
- cptr=line+(strlen(line))-1;
- if ((*cptr=='\r')||(*cptr=='\n'))
- *cptr=0;
- else
- break;
- }
- // ...and add back the gunk that windows likes
- strcat(line,"\r\r\n");
-
- SendDlgItemMessage(hwnd, IDC_TEXT, EM_SETSEL, (WPARAM)lastsel, (LPARAM)lastsel );
- SendDlgItemMessage(hwnd, IDC_TEXT, EM_REPLACESEL, 0, (LPARAM)(line) );
- SendDlgItemMessage(hwnd, IDC_TEXT, EM_GETSEL, (WPARAM)NULL, (LPARAM)&lastsel );
- }
- unselectText=1;
- fclose(in);
- return(1); // 1 means windows handles focus issues
- }
- break;
- case WM_PAINT:
- if (unselectText)
- SendDlgItemMessage(hwnd, IDC_TEXT, EM_SETSEL, -1, 0);
- unselectText=0;
- return(0);
- break;
- case WM_COMMAND:
- switch(wParam) {
- case IDOK:
- {
- EndDialog(hwnd,0);
- return(1);
- }
- default:
- break;
- }
- default:
- break;
- case WM_CLOSE:
- EndDialog(hwnd,0);
- return(1);
- break;
- }
- return(FALSE);
- }
- // Restart the computer for certain types of patches
- void Shutdown_Computer_Now(void);
- LPVOID CALLBACK __export PatchCallBack(UINT ID, LPVOID Param);
- typedef LPVOID (CALLBACK* PATCHCALLBACK)(UINT, LPVOID);
- typedef UINT (CALLBACK *PATCHFUNC)( LPSTR, PATCHCALLBACK, BOOL);
- //
- // Apply any type of patch. Filename in patchfile. Product base registry
- // (eg: "SOFTWARE\Westwood\Red Alert") should be in the config file as
- // SKUX SKU base reg dir where X = index
- //
- void Apply_Patch(char *patchfile,ConfigFile &config,int skuIndex)
- {
- DBGMSG("PATCHFILE : "<<patchfile);
- char cwdbuf[256];
- _getcwd(cwdbuf,255);
- DBGMSG("CWD : "<<cwdbuf);
- //
- // If patch is a .exe type patch
- //
- if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exe"),".exe")==0)
- {
- // Set this as a run once service thing
- HKEY regKey;
- LONG regRetval;
- DWORD regPrevious;
- regRetval=RegCreateKeyEx(
- HKEY_LOCAL_MACHINE,
- "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
- 0,
- "",
- REG_OPTION_NON_VOLATILE,
- KEY_ALL_ACCESS,
- NULL,
- ®Key,
- ®Previous);
- if (regRetval==ERROR_SUCCESS)
- {
- RegSetValueEx(regKey,"EXEPatch",0,REG_SZ,(const uint8*)patchfile,strlen(patchfile)+1);
- char message[256];
- LoadString(NULL,IDS_SYS_RESTART,message,256);
- char title[128];
- LoadString(NULL,IDS_SYS_RESTART_TITLE,title,128);
- MessageBox(NULL,message,title,MB_OK);
- Shutdown_Computer_Now();
- }
- else
- {
- char message[256];
- LoadString(NULL,IDS_RUNONCE_ERR,message,256);
- char string[256];
- sprintf(string,message,patchfile);
- MessageBox(NULL,string,"ERROR",MB_OK);
- }
- }
- //
- // RTPatch type patch
- //
- else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".rtp"),".rtp")==0)
- {
- MSG msg;
- HWND dialog=Create_Patch_Dialog();
- while(PeekMessage(&msg,NULL,0,0, PM_REMOVE))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- HINSTANCE hInst=LoadLibrary("patchw32.dll");
- if (hInst==NULL)
- {
- char message[256];
- LoadString(NULL,IDS_ERR_MISSING_FILE,message,256);
- char string[256];
- sprintf(string,message,"patchw32.dll");
- char title[128];
- LoadString(NULL,IDS_ERROR,title,128);
- MessageBox(NULL,string,title,MB_OK);
- return;
- }
- DBGMSG("Patch SKU = "<<skuIndex);
- PATCHFUNC patchFunc;
- patchFunc=(PATCHFUNC)GetProcAddress(hInst,"RTPatchApply32@12");
- if (patchFunc==NULL)
- {
- char message[256];
- LoadString(NULL,IDS_BAD_LIBRARY,message,256);
- char title[128];
- LoadString(NULL,IDS_ERROR,title,128);
- MessageBox(NULL,message,title,MB_OK);
- return;
- }
- char patchArgs[200];
- sprintf(patchArgs,"\"%s\" .",patchfile);
- int rtpErrCode=patchFunc(patchArgs,PatchCallBack,TRUE);
- FreeLibrary(hInst); // unload the DLL
- _unlink(patchfile); // delete the patch
- DestroyWindow(dialog); // get rid of the dialog
- // Now we have to update the registry so the version is correct
- // The version is the first integer in the filename
- // Eg: 22456_patch.rtp means version 22456 goes into the registry
- // The version# starts after the last '\' char
- char *cptr=patchfile;
- char *tempPtr;
- DWORD version;
- while( (tempPtr=strchr(cptr,'\\')) !=NULL)
- cptr=tempPtr+1;
- if (cptr)
- version=atol(cptr);
- DBGMSG("VERSION TO = "<<version);
-
- char string[256];
- Wstring key;
- // Get the InstallPath from the specified registry key
- sprintf(string,"SKU%d",skuIndex);
- if (config.getString(string,key)==FALSE)
- {
- ERRMSGX("SKU is missing from config file!", "D:\\dev\\launcher\\patch.cpp"); //ERRMSG("SKU is missing from config file!");
- return;
- }
- int temp=0;
- Wstring sku;
- Wstring path;
- temp=key.getToken(temp," ",sku);
- path=key;
- path.remove(0,temp);
- while((*(path.get()))==' ') // remove leading spaces
- path.remove(0,1);
- // Open the registry key for modifying now...
- HKEY regKey;
- LONG regRetval;
- regRetval=RegOpenKeyEx(HKEY_LOCAL_MACHINE,path.get(),0,
- KEY_ALL_ACCESS,®Key);
- if (regRetval!=ERROR_SUCCESS)
- DBGMSG("Can't open reg key for writing");
- regRetval=RegSetValueEx(regKey,"Version",0,REG_DWORD,(uint8 *)&version,
- sizeof(version));
- // Create blocking DLG for update info
- DialogBox(Global_instance,MAKEINTRESOURCE(IDD_CHANGELOG),NULL,(DLGPROC)Update_Info_Proc);
- }
- //
- // Execute now (without rebooting) type patch
- //
- else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".exn"),".exn")==0)
- {
- Process proc;
- strcpy(proc.directory,".");
- strcpy(proc.command,patchfile);
- Create_Process(proc);
- Wait_Process(proc);
- _unlink(patchfile);
- }
- //
- // Web link type patch
- //
- // Im about 99.44% sure that this is completely useless.
- //
- else if (strcasecmp(patchfile+strlen(patchfile)-strlen(".web"),".web")==0)
- {
- char message[256];
- LoadString(NULL,IDS_WEBPATCH,message,256);
- char title[128];
- LoadString(NULL,IDS_WEBPATCH_TITLE,title,128);
- MessageBox(NULL,message,title,MB_OK);
- FILE *in=fopen(patchfile,"r");
- if (in!=NULL)
- {
- char URL[256];
- fgets(URL,255,in);
- fclose(in);
- ShellExecute(NULL,NULL,URL,NULL,".",SW_SHOW);
- _unlink(patchfile);
- //// This is somewhat skanky, but we can't wait
- //// for the viewer to exit (I tried).
- exit(0);
- }
- else
- {
- MessageBox(NULL,patchfile,"Patchfile vanished?",MB_OK);
- }
- }
- }
- void Shutdown_Computer_Now(void)
- {
- HANDLE hToken;
- TOKEN_PRIVILEGES tkp;
-
- // Get a token for this process.
- if (!OpenProcessToken(GetCurrentProcess(),
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
- {
- //error("OpenProcessToken");
- }
-
- // Get the LUID for the shutdown privilege.
- LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
- &tkp.Privileges[0].Luid);
-
- tkp.PrivilegeCount = 1; // one privilege to set
- tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- // Get the shutdown privilege for this process.
- AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
- (PTOKEN_PRIVILEGES)NULL, 0);
-
- // Cannot test the return value of AdjustTokenPrivileges.
- if (GetLastError() != ERROR_SUCCESS)
- {
- //error("AdjustTokenPrivileges");
- }
-
- // Shut down the system and force all applications to close.
- if (!ExitWindowsEx(EWX_REBOOT, 0))
- {
- // Should never happen
- char restart[128];
- LoadString(NULL,IDS_MUST_RESTART,restart,128);
- MessageBox(NULL,restart,"OK",MB_OK);
- exit(0);
- }
- MSG msg;
- while (GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage( &msg );
- DispatchMessage( &msg );
- }
- }
- //
- // Callback during the patching process
- //
- LPVOID CALLBACK __export PatchCallBack(UINT Id, LPVOID Param)
- {
- char string[128];
- static int fileCount=0; // number of files to be patched
- static int currFile=0;
- // Make sure our windows get updated
- MSG msg;
- int counter=0;
- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ))
- {
- TranslateMessage( &msg );
- DispatchMessage( &msg );
- counter++;
- if (counter==100) // just in case...
- break;
- }
- LPVOID RetVal="";
- bit8 Abort=FALSE;
- //// using the global Dialog pointer, set the current "error" code
- //g_DlgPtr->SetRTPErrCode(Id);
- int percent;
- switch( Id )
- {
- case 1:
- case 2:
- // Warning message header/text
- DBGMSG("P_MSG: "<<((char *)Param));
- break;
- case 3:
- // Error message header
- DBGMSG("P_MSG: "<<((char *)Param));
- break;
- case 4:
- // Error message header/text
- ///////*g_LogFile << (char *)Parm << endl;
- MessageBox(NULL,(char *)Param,"ERROR",MB_OK);
- {
- FILE *out=fopen("patch.err","a");
- time_t timet=time(NULL);
- fprintf(out,"\n\nPatch Erorr: %s\n",ctime(&timet));
- fprintf(out,"%s\n",(char *)Param);
- fclose(out);
- }
- break;
- case 9:
- // progress message
- break;
- case 0xa:
- // help message
- break;
- case 0xb:
- // patch file comment
- break;
- case 0xc:
- // copyright message
- break; // these just display text
- case 5:
- // % completed
- // so adjust the progress bar using the global Dialog pointer
- /////////g_DlgPtr->SetProgressBar((int)((float)(*(UINT *)Parm)/(float)0x8000*(float)100));
- percent=((*(UINT *)Param)*100)/0x8000;
- SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
- break;
- case 6:
- // Number of patch files
- DBGMSG("6: "<<*((uint32 *)Param));
- fileCount=*((uint32 *)Param);
- currFile=0;
- break;
- case 7:
- //// begin patch
- //LoadString(g_AppInstance, IDS_PROCESSING, lpcBuf, 256);
- //strcpy(buf,lpcBuf);
- //strcat(buf,(char *)Parm);
- //g_DlgPtr->SetProgressText(buf);
- //*g_LogFile << buf << " : ";
- //fileModified = true;
- DBGMSG("7: "<<(char *)Param);
- SetWindowText(GetDlgItem(PatchDialog,IDC_FILENAME),(char *)Param);
- percent=0;
- SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
- currFile++;
- char xofy[64];
- LoadString(NULL,IDS_FILE_X_OF_Y,xofy,64);
- sprintf(string,xofy,currFile,fileCount);
- SetWindowText(GetDlgItem(PatchDialog,IDC_CAPTION),string);
- break;
- case 8:
- //// end patch
- //LoadString(g_AppInstance, IDS_PROCCOMPLETE, lpcBuf, 256);
- //g_DlgPtr->SetProgressText(lpcBuf);
- //*g_LogFile << " complete" << endl;
- percent=100;
- SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0);
- DBGMSG("P_DONE");
- break;
- case 0xd:
- //// this one shouldn't happen (only occurs if the command line
- //// doesn't have a patch file in it, and we insure that it does).
- //Abort = TRUE;
- //*g_LogFile << "Incorrect (or none) patch file specified in command line." << endl;
- break;
- case 0xe:
- //// this one shouldn't happen either (same reason)
- //Abort = TRUE;
- //*g_LogFile << "Incorrect (or none) path specified in command line." << endl;
- break;
- case 0xf:
- //// Password Dialog
- break;
- case 0x10:
- //// Invalid Password Alert
- break;
- case 0x11:
- //// Disk Change Dialog
- break;
- case 0x12:
- //// Disk Change Alert
- break;
- case 0x13:
- //// Confirmation Dialog
- break;
- case 0x14:
- //// Location Dialog
- //Abort = TRUE;
- //*g_LogFile << "Specified path is incorrect." << endl;
- break;
- case 0x16:
- //// Searching Call-back
- break;
- case 0x15:
- //// Idle...
- break;
- default:
- break;
- }
- if(Abort)
- return (NULL);
- else
- return (RetVal);
- }
|