Files
firestorm/Gameleap/code/mw4/Tools/PatchIT/PatchApply/PatchApplyDlg.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

693 lines
15 KiB
C++

// PatchApplyDlg.cpp : implementation file
//
#include "stdafx.h"
#include "PatchApply.h"
#include "PatchApplyDlg.h"
#include "patchwin.h"
#define PATCHFILE "mw4.rtp"
extern bool EnglishErrors;
#define LNG_ENGLISH 0x0409
#define LNG_FRENCH 0x040C
#define LNG_GERMAN 0x0407
#define LNG_KOREAN 0x0412
#define LNG_SPANISH 0x0C0A
#define LNG_CHINESE 0x0404
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
void PatchApply(const char *);
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPatchApplyDlg dialog
CPatchApplyDlg *g_DlgPtr;
CPatchApplyDlg::CPatchApplyDlg(CWnd* pParent /*=NULL*/)
: CDialog(CPatchApplyDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CPatchApplyDlg)
m_StatText = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
g_DlgPtr=this;
InSetup=false;
IsEnglish=false;
Patching=false;
ErrorProcessed=false;
}
void CPatchApplyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPatchApplyDlg)
DDX_Control(pDX, IDC_FILEPROG, m_FileProg);
DDX_Control(pDX, IDC_EXIT, m_But);
DDX_Control(pDX, IDC_PATCHPROG, m_PatchProg);
DDX_Text(pDX, IDC_STATTEXT, m_StatText);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CPatchApplyDlg, CDialog)
//{{AFX_MSG_MAP(CPatchApplyDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_EXIT, OnExit)
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPatchApplyDlg message handlers
BOOL CPatchApplyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
SetWindowText(GetTransString(IDS_ENG_WINTITLE));
m_But.SetWindowText(GetTransString(IDS_ENG_EXIT));
IsEnglish=false;
Patching=false;
m_PatchProg.SetRange(0,100);
m_PatchProg.SetPos(0);
m_FileProg.SetPos(0);
ErrorProcessed=false;
CRect deskrect,rect;
SystemParametersInfo(SPI_GETWORKAREA, NULL, (LPVOID) &deskrect, false);
GetWindowRect(rect);
SetWindowPos(NULL,deskrect.CenterPoint().x-rect.Width()/2,
deskrect.CenterPoint().y-rect.Height()/2,-1,-1,SWP_NOZORDER|SWP_NOSIZE);
ShowWindow(SW_SHOW);
if(InSetup|| (MessageBox(GetTransString(IDS_ENG_PTTIT),GetTransString(IDS_ENG_PTMSG),MB_OKCANCEL)==IDOK))
{
ApplyPatch();
}
else
PostQuitMessage(0);
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CPatchApplyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CPatchApplyDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CPatchApplyDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} ;
LPVOID CALLBACK CallBack( UINT Id, LPVOID Parm)
{
return g_DlgPtr->PatchCallBack(Id,Parm);
//MessageBox(NULL,(LPCSTR)Parm,"RTPatch",MB_OK);
return NULL;
}
void CPatchApplyDlg::ApplyPatch()
{
Patching=true;
m_But.EnableWindow(FALSE);
HKEY pathkey;
if(ERROR_SUCCESS !=RegOpenKey(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Microsoft Games\\MechWarrior Vengeance",&pathkey))
{
FatalError(IDS_ENG_REGERROR);
return;
}
DWORD ktype;
DWORD size= MAX_PATH;
char dbuf[MAX_PATH];
if(ERROR_SUCCESS !=RegQueryValueEx(pathkey,"EXE Path",NULL,&ktype,(BYTE *)dbuf,&size) || size<=0 || ktype!=REG_SZ)
{
FatalError(IDS_ENG_REGERROR);
return;
}
RegCloseKey(pathkey);
CString game_path(dbuf);
CString file_path;
m_StatText=GetTransString(IDS_ENG_PTLSF); UpdateData(FALSE);
file_path=game_path+"\\MissionLang.dll";
DWORD bufsize=GetFileVersionInfoSize((char *)(LPCSTR)file_path,NULL);
if(!bufsize)
{
FatalError(IDS_ENG_REGERROR);
return;
}
BYTE *pBlock=new BYTE[bufsize];
GetFileVersionInfo((char *)(LPCSTR)file_path,NULL,bufsize,pBlock);
void *lpBuffer;
UINT dwBytes;
// Read the list of languages and code pages.
UINT cbTranslate;
LANGANDCODEPAGE *lpTranslate;
VerQueryValue(pBlock,
TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&lpTranslate,
&cbTranslate);
// Read the file description for each language and code page.
char SubBlock[512];
wsprintf( SubBlock,
TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
lpTranslate->wLanguage,
lpTranslate->wCodePage);
// Retrieve file description for language and code page "i".
VerQueryValue(pBlock,
SubBlock,
&lpBuffer,
&dwBytes);
CString source_dir;
switch(lpTranslate->wLanguage)
{
case LNG_ENGLISH: source_dir="English"; break;
case LNG_FRENCH: source_dir="French"; break;
case LNG_GERMAN: source_dir="German"; break;
case LNG_KOREAN: source_dir="Korean"; break;
case LNG_SPANISH: source_dir="Spanish"; break;
case LNG_CHINESE: source_dir="Taiwan"; break;
default:
FatalError(IDS_ENG_UDL);
}
//copy or patch files from sourcedir here;
// CopyFile(source_dir+"\\missionlang.dll",game_path+"\\missionlang.dll",NULL);
// CopyFile(source_dir+"\\ScriptStrings.dll",game_path+"\\ScriptStrings.dll",NULL);
delete pBlock;
char tstr[MAX_PATH];
CString old_cwd;
GetCurrentDirectory(MAX_PATH,tstr);
old_cwd=tstr;
source_dir=old_cwd+"\\"+source_dir;
SetCurrentDirectory(game_path);
m_StatText=GetTransString(IDS_ENG_PGF); UpdateData(FALSE);
//Apply Pros/exe Patch here
CString cmdline;
WIN32_FIND_DATA finddat;
cmdline.Format("\"%s\" \"%s\\mw4\"",game_path,source_dir);
if(INVALID_HANDLE_VALUE==FindFirstFile(source_dir+"\\"+PATCHFILE,&finddat))
{
if(InSetup && lpTranslate->wLanguage!=LNG_ENGLISH)
{
FatalError(IDS_LANGMISSING);
}
else
FatalError(IDS_ENG_PATCHERROR);
SetCurrentDirectory(tstr);
return;
}
UINT res=RTPatchApply32((char *)(LPCSTR)cmdline,CallBack,0);
if(res)
{
FatalError(IDS_ENG_PATCHERROR);
SetCurrentDirectory(tstr);
return;
}
SetCurrentDirectory(tstr);
/*
HKEY verkey;
if(ERROR_SUCCESS !=RegOpenKey(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Microsoft Games\\MechWarrior Vengeance",&verkey))
{ FatalError(IDS_ENG_REGERROR); return ; }
if(ERROR_SUCCESS !=RegSetValueEx(verkey,"Version",NULL,REG_SZ,(BYTE *)"4.0a",strlen("4.0a")+1))
{ FatalError(IDS_ENG_REGERROR); return; }
RegCloseKey(verkey);
*/
m_PatchProg.SetPos(100);
m_StatText=GetTransString(IDS_ENG_OPCOM); UpdateData(FALSE);
m_But.EnableWindow(TRUE);
DisplayReadMe();
Patching=false;
OnOK();
}
bool CPatchApplyDlg::NeedUpgrade()
{
bool ret=false;
char str[256];
DWORD len=256;
DWORD type=REG_SZ;
HKEY verkey;
if(ERROR_SUCCESS !=RegOpenKey(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Microsoft Games\\MechWarrior Vengeance",&verkey))
{ ret=true; }
else
if(ERROR_SUCCESS !=RegQueryValueEx(verkey,"Version",NULL,&type,(BYTE *)str,&len))
{ ret=true; }
else
{
if(len<=0 || strcmp(str,"4.0c")<0) ret=true;
else ret=false;
}
RegCloseKey(verkey);
return ret;
}
static LPCSTR LoadStringEx(UINT id)
{
if(!EnglishErrors)
{
LANGID lid=GetSystemDefaultLangID();
HRSRC hres = FindResourceEx(NULL, RT_STRING, MAKEINTRESOURCE((id >> 4) + 1), lid);
if (hres) {
HGLOBAL hmem = LoadResource(NULL, hres);
if (hmem) {
LPWSTR pwz = (LPWSTR)LockResource(hmem);
for (UINT u = id & 15; u > 0; u--)
pwz += *pwz + 1;
if (!*pwz)
return NULL;
static char szMultiByte[1024];
int iRc = WideCharToMultiByte(CP_ACP, 0, pwz + 1, *pwz, szMultiByte, sizeof(szMultiByte), NULL, NULL);
szMultiByte[iRc] = 0;
return szMultiByte;
}
}
DWORD dwErr = GetLastError();
}
CString str;
str.LoadString(id);
return str;
}
CString CPatchApplyDlg::GetTransString(UINT id)
{
CString str;
#if 0
UINT baseid;
LCID lid=GetSystemDefaultLCID();
switch(lid)
{
case LNG_FRENCH: baseid=IDS_FRN_TABLESTART; break;
case LNG_GERMAN: baseid=IDS_GER_TABLESTART; break;
case LNG_KOREAN: baseid=IDS_KOR_TABLESTART; break;
case LNG_SPANISH: baseid=IDS_SPN_TABLESTART; break;
case LNG_CHINESE: baseid=IDS_TAI_TABLESTART; break;
default:
case LNG_ENGLISH: baseid=IDS_ENG_TABLESTART; IsEnglish=true; break;
}
str.LoadString((id-IDS_ENG_TABLESTART)+baseid);
#endif
str = LoadStringEx(id);
#if 0
LCID lcid = GetThreadLocale();
char szString[1024];
int iRc = ::LoadString(AfxGetInstanceHandle(), id, szString, sizeof(szString));
if (iRc)
str = szString;
#endif
return str;
}
void CPatchApplyDlg::FatalError(UINT id)
{
if(!ErrorProcessed)
MessageBox(GetTransString(id)+"\n"+GetTransString(IDS_ENG_UCP),GetTransString(IDS_ENG_ERROR),MB_OK|MB_ICONERROR);
PostQuitMessage(0);
}
bool CPatchApplyDlg::DoesFileExist(LPCSTR fname)
{
return 0xffffffff!=GetFileAttributes( fname );
}
bool CPatchApplyDlg::DisplayReadMe()
{
CString readmefile;
readmefile.LoadString(IDS_READMEFNAME);
if(DoesFileExist( readmefile ))
{
SHELLEXECUTEINFO info;
ZeroMemory(&info,sizeof(info));
info.cbSize=sizeof(info);
info.lpVerb="open";
info.lpFile=readmefile;
info.nShow=SW_SHOWNORMAL;
info.fMask=SEE_MASK_NOCLOSEPROCESS;
ShellExecuteEx(&info);
DWORD eflag;
do
{
if(GetExitCodeProcess(info.hProcess,&eflag)==0) return true;
}
while(eflag==STILL_ACTIVE);
return true;
}
return false;
}
void CPatchApplyDlg::OnExit()
{
OnOK();
}
#define MAX_MESSAGES 10
LPVOID CPatchApplyDlg::PatchCallBack( UINT Id, LPVOID Parm)
{
MSG msg;
UINT MsgCount;
LPVOID RetVal;
// CallBackParam = Parm;
// InCallBack=TRUE; // this flag is checked by our message loop
// to insure that it doesn't respond to the
// menu and try to start one patch in the middle
// of another one...
RetVal = "";
// do a few messages while we're here...
for ( MsgCount = MAX_MESSAGES;
MsgCount && PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
MsgCount--)
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
switch( Id )
{
case 1:
// Warning message header
break;
case 2:
// Warning message text
break;
case 3:
if(!IsEnglish)
{
CString errnum=(char *) Parm;
errnum=errnum.Mid(10,4);
MessageBox(GetTransString(IDS_ENG_SEETXT)+"\n"+errnum+"\n"+GetTransString(IDS_ENG_UCP),GetTransString(IDS_ENG_ERROR),MB_OK|MB_ICONERROR);
ErrorProcessed=true;
PostQuitMessage(0);
// Error message header
}
break;
case 4:
// Error message text
if(IsEnglish)
{
CString err=(char *) Parm;
err+=GetTransString(IDS_ENG_UCP);
MessageBox(err,"RTP Error",MB_OK|MB_ICONERROR);
ErrorProcessed=true;
PostQuitMessage(0);
}
break;
case 9:
// progress message
case 0xa:
// help message
case 0xb:
// patch file comment
case 0xc:
// copyright message
// WriteIt( (char *) Parm ); // for all these callbacks, we just
break; // write the associated text to the
// text pane
case 5:
// % completed
{
int prog=(int) (((long)(0xffff & *((UINT *) Parm))*100L)/0x8000L);
m_PatchProg.SetPos( prog);
}
break;
case 6:
{
int pnum=*((UINT *) Parm);
m_FileProg.SetRange(0,pnum);
}
// Number of patch files
// PATCHGUI ignores this
break;
case 7:
// begin patch
{
CString str=GetTransString(IDS_ENG_PROC);
str+=(char *) Parm ;
m_StatText=str;
UpdateData(FALSE);
m_PatchProg.SetPos(0);
}
break;
case 8:
{
// end patch
m_StatText="";
UpdateData(FALSE);
m_PatchProg.SetPos(0);
m_FileProg.SetPos(m_FileProg.GetPos()+1);
}
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).
break;
case 0xe:
// this one shouldn't happen either (same reason)
break;
case 0xf:
// Password Dialog
break;
case 0x10:
// Invalid Password Alert
break;
case 0x11:
break;
case 0x12:
break;
case 0x13:
break;
case 0x14:
break;
case 0x16:
break;
case 0x15:
// Idle...
default:
break;
}
// do a few more messages while we're here...
for ( MsgCount = MAX_MESSAGES;
MsgCount && PeekMessage( &msg, NULL, 0, 0, PM_REMOVE );
MsgCount--)
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return (RetVal);
}
void CPatchApplyDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
if(!Patching) CDialog::OnClose();
}