/* ** 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/Utilities.cpp $* * * * $Author:: Ian_l $* * * * $Modtime:: 1/11/02 10:27a $* * * * $Revision:: 10 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ // Includes. #include "Utilities.h" #include "ErrorHandler.h" #include "Resource.h" #include "Translator.h" #include #include #include #include // Private prototypes. static bool Create_Directory (const WCHAR *drive, const WCHAR *directory, WCHAR *subdirectory, bool remove, DynamicVectorClass *log); static WCHAR *Standardize_Path (const WideStringClass &path, WideStringClass &standardpath); static bool Validate_Install_CD (const char driveletter, const WideStringClass &installvolumename); /*********************************************************************************************** * Prompt_Install_CD -- Is the install CD in one of the CD drives? If not prompt for it. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Prompt_Install_CD (const WideStringClass &installvolumename, char &sourcedriveletter) { bool success = false; while (!success) { for (char r = 'A'; r <= 'Z'; r++) { char rootpathname [] = "?:\\"; rootpathname [0] = r; // If the drive is a CD-ROM... if (GetDriveType (rootpathname) == DRIVE_CDROM) { if (Validate_Install_CD (r, installvolumename)) { sourcedriveletter = r; success = true; break; } } } if (!success) { bool cancel; // No CD-ROM with the correct label found. Ask user to insert the CD-ROM. ShowCursor (true); cancel = MessageBoxExW (NULL, TxWideStringClass (IDS_INSERT_GAME_CD_ROM), TxWideStringClass (IDS_APPLICATION_NAME), MB_RETRYCANCEL | MB_APPLMODAL | MB_SETFOREGROUND, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT)) == IDCANCEL; ShowCursor (false); if (cancel) break; } } return (success); } /*********************************************************************************************** * Validate_Install_CD -- Is the install CD in the designated CD drive? * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Validate_Install_CD (const WideStringClass &sourcepath, const WideStringClass &installvolumename) { StringClass multibytesourcepath (sourcepath); return (Validate_Install_CD (multibytesourcepath [0], installvolumename)); } /*********************************************************************************************** * Validate_Install_CD -- Is the install CD in the designated CD drive? * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Validate_Install_CD (const char driveletter, const WideStringClass &installvolumename) { char multibytevolumename [_MAX_PATH]; char filesystemname [_MAX_PATH]; DWORD maxfilenamelength; DWORD flags; char rootpathname [] = "?:\\"; rootpathname [0] = driveletter; if (GetVolumeInformation (rootpathname, multibytevolumename, _MAX_PATH, NULL, &maxfilenamelength, &flags, filesystemname, _MAX_PATH)) { WideStringClass volumename (multibytevolumename); // Windows '95 appears to have a volume name limit of 11 characters. I cannot find a Win32 // call that will return the maximum volume name length so the value '11' is hard-coded here // and the assumption made that all OS's have this length or better. if (wcsncmp (volumename, installvolumename, MAX (11, volumename.Get_Length())) == 0) { return (true); } } return (false); } /*********************************************************************************************** * Get_Disk_Space_Available -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Get_Disk_Space_Available (const WideStringClass &path, __int64 &diskspace) { StringClass multibytepath (path); char drive [_MAX_DRIVE]; ULARGE_INTEGER freebytecount; // Free bytes on disk available to caller (caller may not have access to entire disk). ULARGE_INTEGER totalbytecount; // Total bytes on disk. StringClass kernelpathname; int (__stdcall *getfreediskspaceex) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); // Extract the drive. // NOTE 0: Even though GetDiskFreeSpaceEx() will accept a full path, it will err if the path is not valid. // NOTE 1: _splitpath() automatically handles multi-byte character strings. _splitpath (multibytepath, drive, NULL, NULL, NULL); GetSystemDirectory (kernelpathname.Get_Buffer (_MAX_PATH), _MAX_PATH); kernelpathname += "\\"; kernelpathname += "Kernel32.dll"; getfreediskspaceex = (int (_stdcall*) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)) GetProcAddress (GetModuleHandle (kernelpathname.Peek_Buffer()), "GetDiskFreeSpaceExA"); if (getfreediskspaceex != NULL) { // NOTE: This function uses GetDiskFreeSpaceEx() and therefore assumes Win '95 OSR2 or greater. if (!getfreediskspaceex (drive, &freebytecount, &totalbytecount, NULL)) return (false); // Convert to a 64-bit integer. diskspace = freebytecount.QuadPart; } else { DWORD sectorspercluster, bytespersector, freeclustercount, totalclustercount; // The Ex version is not available. Use the Win'95 version. // QUESTION: SDK docs say that values returned by this function are erroneous if partition > 2Gb. // Does that mean that the partition is guaranteed to be <= 2Gb if Ex is not available? if (!GetDiskFreeSpace (drive, §orspercluster, &bytespersector, &freeclustercount, &totalclustercount)) return (false); diskspace = sectorspercluster * bytespersector * freeclustercount; } return (true); } /*********************************************************************************************** * Validate_Path -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Validate_Path (const WideStringClass &path, int &errorcode) { bool valid = false; // Check that path has syntax :\\... if (path.Get_Length() > _MAX_DRIVE) { if (((path [0] >= L'A') && (path [0] <= L'Z')) || ((path [0] >= L'a') && (path [0] <= L'z'))) { if ((path [1] == L':') && ((path [2] == L'\\') || (path [2] == L'/'))) { const WCHAR *c = ((WideStringClass) path).Peek_Buffer() + _MAX_DRIVE; while (*c != L'\0') { if ((*c == L'\\') || (*c == L'/')) { // Cannot have two adjacent slashes. if ((*(c - 1) == L'\\') || (*(c - 1) == L'/')) break; } c++; } // Parsed entire string? if (*c == L'\0') { // Check that path does not contain any illegal characters. if (wcscspn (((WideStringClass) path).Peek_Buffer(), L"*?<>|") == (size_t) path.Get_Length()) { WCHAR drive [_MAX_DRIVE + 1]; WideStringClass directory; wcsncpy (drive, path, _MAX_DRIVE); drive [_MAX_DRIVE] = L'\0'; directory = ((WideStringClass) path).Peek_Buffer() + _MAX_DRIVE; // Attempt to create (and then remove) the path. if (Create_Directory (drive, directory.Peek_Buffer(), directory.Peek_Buffer(), true, NULL)) { valid = true; } else { errorcode = IDS_CANNOT_CREATE_DIRECTORY; } } else { errorcode = IDS_INVALID_PATH; } } else { errorcode = IDS_INVALID_PATH; } } else { errorcode = IDS_INVALID_PATH; } } else { errorcode = IDS_INVALID_PATH; } } else { errorcode = IDS_INVALID_PATH; } return (valid); } /*********************************************************************************************** * Create_Directory -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Create_Directory (const WideStringClass &path, DynamicVectorClass *log) { WCHAR drive [_MAX_DRIVE + 1]; WideStringClass directory; // NOTE: Assumes that path is a full path. If not, an error may be thrown. wcsncpy (drive, path, _MAX_DRIVE); drive [_MAX_DRIVE] = L'\0'; directory = ((WideStringClass) path).Peek_Buffer() + _MAX_DRIVE; return (Create_Directory (drive, directory.Peek_Buffer(), directory.Peek_Buffer(), false, log)); } /*********************************************************************************************** * Create_Directory -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Create_Directory (const WCHAR *drive, const WCHAR *directory, WCHAR *subdirectory, bool remove, DynamicVectorClass *log) { bool valid; WCHAR *c, replacement; WideStringClass path; StringClass multibytepath; DWORD errorcode; // If subdirectory is empty, we're done. c = subdirectory; if (*c != L'\0') { // Step down the subdirectory to the next slash or null. while ((*c != L'\\') && (*c != L'/') && (*c != L'\0')) c++; replacement = *c; *c = L'\0'; path = drive; path += directory; path.Convert_To (multibytepath); if (CreateDirectory (multibytepath, NULL)) { errorcode = 0; if (log != NULL) log->Add (multibytepath); } else { errorcode = GetLastError(); } // If no error or directory already exists... if ((errorcode == 0) || (errorcode == ERROR_ALREADY_EXISTS)) { // Recurse if not at end of string. if (replacement != L'\0') { *c = replacement; valid = Create_Directory (drive, directory, c + 1, remove, log); } else { valid = true; } // If caller wants it removed (and it didn't already exist) then remove it (ie. just verify that it can be created). if (remove && (errorcode == 0)) { if (!RemoveDirectory (multibytepath)) FATAL_SYSTEM_ERROR; } } else { valid = false; } } else { valid = true; } return (valid); } /*********************************************************************************************** * Cluster_Padding -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ __int64 Cluster_Padding (unsigned filecount) { // NOTE: Currently, it is not known how to obtain (or if it is possible to obtain) the cluster size // of a hard disk. According to MS documentation, the largest possible cluster size of FAT16, // FAT32 and NTFS file systems is 64K (and is more typically 4-16K). Therefore, conservatively // estimate the cluster size as 64K. const unsigned clustersize = 0x10000; __int64 padding = clustersize * filecount; return (padding); } /*********************************************************************************************** * Is_Same_Path -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Is_Same_Path (const WideStringClass &path0, const WideStringClass &path1, bool standardize) { if (standardize) { WideStringClass path0copy, path1copy; Standardize_Path (path0, path0copy); Standardize_Path (path1, path1copy); return (path0copy.Compare (path1copy) == 0); } else { return (path0.Compare (path1) == 0); } } /*********************************************************************************************** * Is_Sub_Path -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Is_Sub_Path (const WideStringClass &path0, const WideStringClass &path1, bool standardize) { WideStringClass path0copy, path1copy; if (standardize) { Standardize_Path (path0, path0copy); Standardize_Path (path1, path1copy); } else { path0copy = path0; path1copy = path1; } if (wcsstr (path0copy, path1copy) != NULL) { if (path0copy.Get_Length() == path1copy.Get_Length()) { return (true); } else { WCHAR c; c = path0copy [path1copy.Get_Length()]; if ((c == L'\\') || (c == L'/')) return (true); } } return (false); } /*********************************************************************************************** * Standardize_Path -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ WCHAR *Standardize_Path (const WideStringClass &path, WideStringClass &standardpath) { WideStringClass t (path); int i, c; // Remove leading white space. i = 0; while (t [i] == L' ') { i++; } if (i > 0) t.Erase (0, i); // Remove trailing white space and any trailing slashes. c = 0; i = t.Get_Length() - 1; while ((i >= 0) && ((t [i] == L' ') || (t [i] == L'\\') || (t [i] == L'/'))) { i--; c++; } if (c > 0) t.Erase (i + 1, c); // Replace back slashes with forward slashes and change to upper case. for (i = 0; i < t.Get_Length(); i++) { if (t [i] == L'/') t [i] = L'\\'; t [i] = towupper (t [i]); } standardpath = t; return (standardpath.Peek_Buffer()); } /*********************************************************************************************** * Remove_Trailing_Name -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ WCHAR *Remove_Trailing_Name (WideStringClass &path) { int i, c; for (i = path.Get_Length() - 1, c = 1; i >= 0; i--, c++) { if ((path [i] == L'\\') || (path [i] == L'/')) { path.Erase (i, c); break; } } return (path.Peek_Buffer()); } /*********************************************************************************************** * Extract_Suffix_Root -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ WCHAR *Extract_Suffix_Root (WideStringClass &path, const WideStringClass &prefixpath) { if (wcsstr (path, prefixpath) == path) { int s, e, c; s = prefixpath.Get_Length(); if ((path [s] == L'\\') || (path [s] == L'/')) s++; if (s > 0) path.Erase (0, s); e = 0; c = path.Get_Length(); for (e = 0, c = path.Get_Length(); c > 0; e++, c--) { if ((path [e] == L'\\') || (path [e] == L'/')) { path.Erase (e, c); break; } } return (path.Peek_Buffer()); } return (NULL); } /*********************************************************************************************** * Extract_Trailing_Name -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ WCHAR *Extract_Trailing_Name (WideStringClass &path) { int i; for (i = path.Get_Length() - 1; i >= 0; i--) { if ((path [i] == L'\\') || (path [i] == L'/')) { path.Erase (0, i + 1); break; } } return (path.Peek_Buffer()); } /*********************************************************************************************** * Directory_Exists -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Directory_Exists (const WideStringClass &path) { StringClass multibytepath (path); DWORD errorcode; if (CreateDirectory (multibytepath, NULL)) { errorcode = 0; } else { errorcode = GetLastError(); } if (errorcode == 0) { if (!RemoveDirectory (multibytepath)) FATAL_SYSTEM_ERROR; return (false); } else { return (errorcode == ERROR_ALREADY_EXISTS); } } /*********************************************************************************************** * Is_System_Directory -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Is_System_Directory (const WideStringClass &path) { const int DirectoryTypes [] = { CSIDL_DESKTOP, // 0x0000 \desktop CSIDL_PROGRAMS, // 0x0002 \Start Menu\Programs CSIDL_PERSONAL, // 0x0005 \Start Menu\My Documents CSIDL_FAVORITES, // 0x0006 Favorites CSIDL_STARTUP, // 0x0007 \Start Menu\Programs\Startup CSIDL_RECENT, // 0x0008 \Recent CSIDL_SENDTO, // 0x0009 \SendTo CSIDL_STARTMENU, // 0x000b \Start Menu CSIDL_DESKTOPDIRECTORY, // 0x0010 \Desktop CSIDL_NETHOOD, // 0x0013 \nethood CSIDL_FONTS, // 0x0014 windows\fonts CSIDL_TEMPLATES, // 0x0015 \Templates CSIDL_COMMON_STARTMENU, // 0x0016 All Users\Start Menu CSIDL_COMMON_PROGRAMS, // 0X0017 All Users\Start Menu\Programs CSIDL_COMMON_STARTUP, // 0x0018 All Users\Start Menu\Programs\Startup CSIDL_COMMON_DESKTOPDIRECTORY, // 0x0019 All Users\Desktop CSIDL_APPDATA, // 0x001a \Application Data CSIDL_PRINTHOOD, // 0x001b \PrintHood CSIDL_COMMON_FAVORITES, // 0x001f All Users\Favorites CSIDL_INTERNET_CACHE, // 0x0020 \Local Settings\Temporary Internet Files CSIDL_COOKIES, // 0x0021 \Cookies CSIDL_HISTORY // 0x0022 \Local Settings\History }; char multibytesystempath [_MAX_PATH]; WideStringClass systempath; bool result; HINSTANCE hinstLib; // Test for Windows directory. if (!GetWindowsDirectory (multibytesystempath, _MAX_PATH)) FATAL_SYSTEM_ERROR; systempath = multibytesystempath; if (Is_Sub_Path (path, systempath)) return (true); // Test for System directory. if (!GetSystemDirectory (multibytesystempath, _MAX_PATH)) FATAL_SYSTEM_ERROR; systempath = multibytesystempath; if (Is_Sub_Path (path, systempath)) return (true); // Get a handle to the DLL module. result = false; hinstLib = LoadLibrary ("shfolder.dll"); if (hinstLib != NULL) { HRESULT (__stdcall * PFNSHGETFOLDERPATHA)(HWND, int, HANDLE, DWORD, LPSTR); PFNSHGETFOLDERPATHA = ( HRESULT ( __stdcall * )(HWND, int, HANDLE, DWORD, LPSTR)) GetProcAddress( hinstLib, "SHGetFolderPathA" ); if (PFNSHGETFOLDERPATHA != NULL) { HRESULT hr = 0; for (int i = 0; i < sizeof (DirectoryTypes) / sizeof(int); i++) { // Get each of the known directories above and compare to the desired directory. hr = (*PFNSHGETFOLDERPATHA)( NULL, DirectoryTypes [i], NULL, 0, multibytesystempath); if (hr == S_OK) { systempath = multibytesystempath; if (Is_Sub_Path (path, systempath)) { result = true; break; } } } } // Free the DLL module. FreeLibrary (hinstLib); } return (result); } /*********************************************************************************************** * Get_Current_Directory -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void Get_Current_Directory (WideStringClass &path) { StringClass multibytepath; if (!GetCurrentDirectory (_MAX_PATH, multibytepath.Get_Buffer (MAX_PATH))) FATAL_SYSTEM_ERROR; path = multibytepath; } /*********************************************************************************************** * Generate_Temporary_Pathname -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool Generate_Temporary_Pathname (const WideStringClass &path, StringClass &multibytetemporarypathname) { StringClass multibytepath; // Create temporary file. multibytepath = path; if (!GetTempFileName (multibytepath, "XXX", 0, multibytetemporarypathname.Get_Buffer (_MAX_PATH))) return (false); return (true); } /*********************************************************************************************** * Message_Box -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void Message_Box (const WideStringClass &header, const WideStringClass &errormessage) { ShowCursor (true); MessageBoxExW (NULL, ((WideStringClass) errormessage).Peek_Buffer(), ((WideStringClass) header).Peek_Buffer(), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT)); ShowCursor (false); }