/*
**	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 <http://www.gnu.org/licenses/>.
*/

/*********************************************************************************************** 
 ***                            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 <malloc.h>
#include <dbt.h>


// 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 <SafeTimerClass> 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);
	}
}
