/*
** Command & Conquer Renegade(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 .
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Installer *
* *
* $Archive:: /Commando/Code/Installer/WinMain.cpp $*
* *
* $Author:: Ian_l $*
* *
* $Modtime:: 1/22/02 2:17p $*
* *
* $Revision:: 12 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Include files.
#include "Argv.h"
#include "Bufffile.h"
#include "Chunkio.h"
#include "ErrorHandler.h"
#include "FFactory.h"
#include "Installer.h"
#include "MixFile.h"
#include "Msgloop.h"
#include "RAMFileFactory.h"
#include "Resource.h"
#include "SafeTimer.h"
#include "SaveLoad.h"
#include "Timer.h"
#include "Translator.h"
#include "Win.h"
#include "WW3D.h"
#include "WWFile.h"
#include
#include
// Defines.
#define AUTORUN_MUTEX_OBJECT TEXT("01AF9993-3492-11d3-8F6F-0060089C05B1")
#define APPLICATION_MUTEX_OBJECT TEXT("C6D925A3-7A9B-4ca3-866D-8B4D506C3665")
// Static variables.
// Foward declarations.
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
ATOM MyRegisterClass (HINSTANCE hInstance);
bool Is_Autorun_Running();
bool Is_Application_Running();
bool Is_Win_95_Or_Above();
bool Is_Win_2K_Or_Above();
bool Running_As_Administrator();
void Prog_End();
/***********************************************************************************************
* WinMain -- Entry point to program. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int result;
// Initialize command line parser.
ArgvClass::Init (lpCmdLine);
TranslateDBClass::Initialize();
try {
// Set global copy of program instance.
ProgramInstance = hInstance;
// Ensure Autorun or another instance of this application is not running.
if ((!Is_Autorun_Running()) && (!Is_Application_Running())) {
// WARNING: Can only raise fatal system errors (not fatal application errors) until
// the translation table has been loaded.
const char *installmixname = "InstallMix.dat";
const char *installstringsfilename = "InstallStrings.tdb";
RAMFileFactoryClass ramfilefactory;
MixFileFactoryClass mixfilefactory (installmixname, &ramfilefactory);
FileClass *file;
// Perform application initialization.
// An invalid mixfile factory indicates a read error.
if (!mixfilefactory.Is_Valid()) FATAL_SYSTEM_ERROR;
_TheFileFactory = &mixfilefactory;
// Load the translation table.
file = _TheFileFactory->Get_File (installstringsfilename);
if (file == NULL) {
// Output an appropriate Windows error message.
SetLastError (2);
FATAL_SYSTEM_ERROR;
}
file->Open (FileClass::READ);
if (file->Is_Available()) {
ChunkLoadClass cload (file);
SaveLoadSystemClass::Load (cload);
} else {
// Output an appropriate Windows error message.
SetLastError (2);
FATAL_SYSTEM_ERROR;
}
file->Close();
_TheFileFactory->Return_File (file);
// Check for valid OS.
if (!(Is_Win_95_Or_Above() || Is_Win_2K_Or_Above())) FATAL_APP_ERROR (IDS_BAD_OS);
// Check for Administrator rights under Win 2k or above.
if (Is_Win_2K_Or_Above()) {
if (!Running_As_Administrator()) FATAL_APP_ERROR (IDS_NOT_ADMINISTRATOR);
}
// Register function to be called at exit.
atexit (Prog_End);
MyRegisterClass (hInstance);
MainWindow = CreateWindow (RxStringClass (IDS_APPLICATION_MAIN_WINDOW),
StringClass (TxWideStringClass (IDS_APPLICATION_NAME)),
WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX|WS_CLIPCHILDREN,
0, 0, 0, 0,
NULL, NULL,
hInstance, NULL);
if (MainWindow == NULL) FATAL_SYSTEM_ERROR;
ShowCursor (false);
_Installer.Install (&mixfilefactory);
ShowCursor (true);
}
result = 1;
} catch (const WideStringClass &errormessage) {
// Catch handler for fatal errors.
DestroyWindow (MainWindow);
Windows_Message_Handler();
Message_Box (TxWideStringClass (IDS_APPLICATION_ERROR), errormessage);
result = 0;
}
TranslateDBClass::Shutdown();
ArgvClass::Free();
return (result);
}
/***********************************************************************************************
* MyRegisterClass -- *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
ATOM MyRegisterClass (HINSTANCE hInstance)
{
static RxStringClass _classname (IDS_APPLICATION_MAIN_WINDOW);
WNDCLASSEX wcex;
wcex.cbSize = sizeof (WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_INSTALLER);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCSTR)IDS_INSTALLER;
wcex.lpszClassName = _classname;
wcex.hIconSm = LoadIcon (hInstance, (LPCTSTR)IDI_INSTALLER);
return RegisterClassEx (&wcex);
}
/***********************************************************************************************
* WndProc -- Windows message handler. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Pass this message through to the input handler. If the message
// was processed and requires no further action, then return with
// this information.
if (_Installer.Get_Input() != NULL) {
LRESULT result = 0;
if (_Installer.Get_Input()->ProcessMessage (hWnd, message, wParam, lParam, result)) {
return (result);
}
}
switch (message)
{
case WM_ACTIVATEAPP:
if (wParam && !GameInFocus) {
GameInFocus = true;
} else {
if (!wParam && GameInFocus) {
GameInFocus = false;
}
}
return (0);
case WM_ERASEBKGND:
return (1);
case WM_PAINT:
ValidateRect (hWnd, NULL);
break;
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_SYSCOMMAND:
switch (wParam) {
case SC_CLOSE:
// Windows sent us a close message - probably in response to Alt-F4. Ignore it.
return (0);
case SC_SCREENSAVE:
// Windows is about to start the screen saver. If we just return without passing
// this message to DefWindowProc then the screen saver will not be allowed to start.
return (0);
case SC_KEYMENU:
// Ignore all "menu-activation" commands.
return (0);
}
break;
case WM_KEYUP:
// Test for SHIFT + ESC.
if ((wParam & 0xff) == VK_ESCAPE && ((GetKeyState (VK_SHIFT) & 0x8000) != 0x0)) {
_Installer.Cancel_Introduction();
}
#if !NDEBUG
// Test for Print Screen (screenshot).
if ((wParam & 0xff) == VK_SNAPSHOT) {
WW3D::Make_Screen_Shot();
}
#endif
case WM_MOUSEWHEEL:
{
if (_Installer.Get_Input() != NULL) {
_Installer.Get_Input()->Add_Mouse_Wheel (HIWORD (wParam));
return (0);
}
break;
}
case WM_DEVICECHANGE:
{
PDEV_BROADCAST_HDR pdbch;
pdbch = (PDEV_BROADCAST_HDR) lParam;
if (pdbch != NULL) {
if (pdbch->dbch_devicetype == DBT_DEVTYP_VOLUME) {
// Assume that this is the CD-ROM drive.
if (wParam == DBT_DEVICEQUERYREMOVE) {
ShowWindow (hWnd, SW_MINIMIZE);
} else {
if (wParam == DBT_DEVICEARRIVAL) {
if (IsIconic (hWnd)) {
ShowWindow (hWnd, SW_RESTORE);
}
SetForegroundWindow (hWnd);
}
}
return (TRUE);
}
}
break;
}
default:
break;
}
return (DefWindowProc (hWnd, message, wParam, lParam));
}
/***********************************************************************************************
* InstallerClass::Is_Autorun_Running -- Determine whether Autorun (a sister application) is *
* running. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
bool Is_Autorun_Running()
{
HANDLE autorunmutex = OpenMutex (MUTEX_ALL_ACCESS & SYNCHRONIZE, FALSE, AUTORUN_MUTEX_OBJECT);
if (autorunmutex != NULL) {
HWND window = FindWindow (RxStringClass (IDS_AUTORUN_MAIN_WINDOW), NULL);
if (window) {
CDTimerClass delaytimer (10 * 1000);
// Hang around for a while to see if Autorun is about to quit...
while ((delaytimer.Value() > 0) && (window != NULL)) {
window = FindWindow (RxStringClass (IDS_AUTORUN_MAIN_WINDOW), NULL);
}
}
CloseHandle (autorunmutex);
// If the Autorun window still exists bring it to the foreground.
if (window) {
if (IsIconic (window)) {
ShowWindow (window, SW_RESTORE);
}
SetForegroundWindow (window);
}
}
return (autorunmutex != NULL);
}
/***********************************************************************************************
* InstallerClass::Is_Application_Running -- Determine whether this application is running. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
bool Is_Application_Running()
{
HANDLE appmutex = CreateMutex (NULL, FALSE, APPLICATION_MUTEX_OBJECT);
if (appmutex == NULL) FATAL_SYSTEM_ERROR;
// See if the application was already running.
HWND prev = FindWindow (RxStringClass (IDS_APPLICATION_MAIN_WINDOW), NULL);
if (prev) {
if (IsIconic (prev)) {
ShowWindow (prev, SW_RESTORE);
}
SetForegroundWindow (prev);
return (true);
} else {
return (false);
}
}
/***********************************************************************************************
* InstallerClass::Is_Win_95_Or_Above -- *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
bool Is_Win_95_Or_Above()
{
OSVERSIONINFO versioninfo;
BOOL result;
bool validos = false;
versioninfo.dwOSVersionInfoSize = sizeof (versioninfo);
result = GetVersionEx (&versioninfo);
if (result) {
if (versioninfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
validos = true;
}
}
return (validos);
}
/***********************************************************************************************
* InstallerClass::Is_Win_2K_Or_Above -- *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
bool Is_Win_2K_Or_Above()
{
OSVERSIONINFO versioninfo;
BOOL result;
bool validos = false;
versioninfo.dwOSVersionInfoSize = sizeof (versioninfo);
result = GetVersionEx (&versioninfo);
if (result) {
if (versioninfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
validos = (versioninfo.dwMajorVersion >= 5) && (versioninfo.dwMinorVersion >= 0);
}
}
return (validos);
}
/***********************************************************************************************
* InstallerClass::Running_As_Administrator -- *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
bool Running_As_Administrator()
{
bool fAdmin;
HANDLE hThread;
TOKEN_GROUPS *ptg = NULL;
DWORD cbTokenGroups;
DWORD dwGroup;
PSID psidAdmin;
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
// Open a handle to the access token for this thread.
if (!OpenThreadToken (GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread)) {
if (GetLastError() == ERROR_NO_TOKEN) {
// If the thread does not have an access token, examine the access token associated with the process.
if (! OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hThread)) {
return (false);
}
} else {
return (false);
}
}
// Query the size of the group information associated with the token. Note that we expect a FALSE result from GetTokenInformation
// because we've given it a NULL buffer. On exit cbTokenGroups will tell the size of the group information.
if (GetTokenInformation (hThread, TokenGroups, NULL, 0, &cbTokenGroups)) {
return (false);
}
// Verify that GetTokenInformation failed for lack of a large enough buffer.
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return (false);
}
// Allocate a buffer for the group information. Since _alloca allocates on the stack, we don't have
// to explicitly deallocate it. That happens automatically when we exit this function.
if (!(ptg = (TOKEN_GROUPS*) _alloca (cbTokenGroups))) {
return (false);
}
// Ask for the group information again. This may fail if an administrator has added this account
// to an additional group between our first call to GetTokenInformation and this one.
if (!GetTokenInformation (hThread, TokenGroups, ptg, cbTokenGroups, &cbTokenGroups)) {
return (false);
}
// Create a System Identifier for the Admin group.
if (!AllocateAndInitializeSid (&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin)) {
return (false);
}
// Iterate through the list of groups for this access token looking for a match against the SID we created above.
fAdmin = false;
for (dwGroup = 0; dwGroup < ptg->GroupCount; dwGroup++) {
if (EqualSid (ptg->Groups[dwGroup].Sid, psidAdmin)) {
fAdmin = true;
break;
}
}
// Explicity deallocate the SID we created.
FreeSid (psidAdmin);
return (fAdmin);
}
/***********************************************************************************************
* Prog_End -- *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/22/01 IML : Created. *
*=============================================================================================*/
void Prog_End()
{
try {
_Installer.On_Prog_End();
} catch (const WideStringClass &errormessage) {
// Catch handler for fatal errors.
DestroyWindow (MainWindow);
Windows_Message_Handler();
Message_Box (TxWideStringClass (IDS_APPLICATION_ERROR), errormessage);
}
}