////////////////////////////////////////////////////////////////////////////////
// MechWarrior installers
// Copyright Bjoern Ganster
// Developed since 2007
////////////////////////////////////////////////////////////////////////////////

#include "BGCPString.h"
#include "BGProcess.h"
#include "Config.h"
#include "dialogs.h"
#include "FileList.h"
#include "Installers.h"
#include "MechVM.h"
#include "MechWarriorIIPRJ.h"
#include "MechWarriorInstallers.h"
#include "MechWarrior3MechImporter.h"
#include "MechWarrior3ZBD.h"
#include "MW2MechImporter.h"
#include "MWBase.h"

#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#include <iostream>

#define FILE_NUMBER FILE_NO_MECHWARRIOR_INSTALLERS_CPP

struct InstallData {
	const PathChar* source;
	int CDtype;
	const PathChar* imgFN;
	Installers* installerWin;
};

////////////////////////////////////////////////////////////////////////////////
// Check if directory source has a certain file or directory

bool directoryExists(const PathChar* source, const char* FN)
{
	SET_SCOPE;
	BGCPString path(source, CP_SYSTEM);
	path.addPathSep(FN);
#ifdef _WIN32
	WIN32_FIND_DATAW info;
	path += OSPathSepStr "*";
	HANDLE h = FindFirstFileW (path.getAsPath(), &info);
	bool found = (h != INVALID_HANDLE_VALUE);
	FindClose(h);
#else
	struct stat fileStat;
	stat (path.getAsPath(), &fileStat);
	bool found = S_ISDIR(fileStat.st_mode);
#endif

	RETURN found;
}

////////////////////////////////////////////////////////////////////////////////

void mkdir (const BGCPString& targetDir, const BGCPString& dirName)
{
	SET_SCOPE;
	BGCPString fullPath = targetDir;
	fullPath.addPathSep(dirName);
	bgmkdir (fullPath.getAsPath());
	RETURN;
}

void copyFile (const PathChar* source, const char* FN, const PathChar* target, 
	const char* targetFN)
{
	SET_SCOPE;
	BGCPString fullSource(source, CP_SYSTEM);
	fullSource.addPathSep(FN);
	BGCPString fullTarget(target, CP_SYSTEM);
	fullTarget.addPathSep(targetFN);

	if (fileExists (fullSource.getAsPath())) {
		copyFile (fullSource.getAsPath(), fullTarget.getAsPath());
	} else {
		printf ("Could not find " PathCharMod "\n", fullSource.getAsPath());
	}
	RETURN;
}

// Copy all files matching mask from source to target
#ifdef _WIN32
void copyFiles (const PathChar* source, const char* mask, const PathChar* target)
{
	SET_SCOPE;
	BGCPString fullsource(source, CP_SYSTEM);
	fullsource.addPathSep(mask);

	WIN32_FIND_DATAW info;
	HANDLE h = FindFirstFileW (fullsource.getAsPath(), &info);
	bool found = (h != INVALID_HANDLE_VALUE);

	while (found) {
		SET_OP;
		bool noDir = ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
		if (noDir) {
			BGCPString sourceStr(source, CP_SYSTEM);
			sourceStr.addPathSep(BGCPString(info.cFileName, CP_SYSTEM));
			BGCPString targetStr(target, CP_SYSTEM);
			targetStr.addPathSep(BGCPString(info.cFileName, CP_SYSTEM));
			copyFile (sourceStr.getAsPath(), targetStr.getAsPath());
		}
		if (!FindNextFileW (h, &info))
			found = false;
	}
	FindClose(h);
	RETURN;
}

#else // Linux

void copyFiles (const char* source, const char* /*mask*/, const TCHAR* target)
{
   struct dirent *dp;
   DIR *dir = opendir(source);
   if (dir == NULL) {
      printf ("%s does not exist - cannot copy\n", source);
      return;
   }

   while ((dp=readdir(dir)) != NULL) {
      BGCPString sourceFN(source, CP_SYSTEM);
      sourceFN.addPathSep(dp->d_name);

      BGCPString targetFN(target, CP_SYSTEM);
      targetFN.addPathSep(dp->d_name);

      //printf("Copying %s to %s\n", sourceFN.getChars(), targetFN.getChars());
      copyFile (sourceFN.getAsPath(), targetFN.getAsPath());
   }

   closedir(dir);
}
#endif

void copyDir (const PathChar* source, const char* subDir, 
              const PathChar* target, const char* targetSubDir)
{
	SET_SCOPE;
	BGCPString fullsource(source, CP_SYSTEM);
	fullsource.addPathSep(subDir);
	BGCPString fulltarget(target, CP_SYSTEM);
	fulltarget.addPathSep(targetSubDir);
	copyFiles (fullsource.getAsPath(), "*", fulltarget.getAsPath());
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////

#ifdef _WIN32
void setWin95Mode (const PathChar* path, const char* exeName, bool color256 = false)
{
	SET_SCOPE;
	const PathChar keyPath[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers";
	BGCPString subKey(path, CP_SYSTEM);
	subKey.addPathSep(exeName);
	HKEY hKey;
	const wchar_t* value = L"WIN95 RUNASADMIN";
	if (color256)
		value = L"WIN95 RUNASADMIN 256COLOR";
	RegCreateKeyExW (HKEY_CURRENT_USER, keyPath, 0, 0, 
		REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, 0);	
	RegSetValueExW (hKey, subKey.getAsPath(), 0, REG_SZ, (BYTE*) value, 
		(DWORD) bgstrlen(value)*sizeof(wchar_t));
	RegCloseKey (hKey);
	RETURN;
}

#else // Linux
void setWin95Mode (const TCHAR* /*path*/, TCHAR* /*exeName*/, bool /*color256*/ = false)
{
}
#endif

////////////////////////////////////////////////////////////////////////////////

#ifdef _WIN32
bool patch42 (const PathChar* target, const char* fn)
{
	SET_SCOPE;
	BGCPString fullFN(target, CP_SYSTEM);
	fullFN.addPathSep(fn);

	MemoryBlock mb;
	if (mb.loadFromFile (fullFN.getAsPath())) {
		printf ("Patching " PathCharMod "\n", fullFN.getAsPath());
		mb.replaceAll ("GDI32.dll", "GDI42.DLL");
		mb.replaceAll ("kernel32.dll", "kernel42.dll");
		mb.replaceAll ("KERNEL32.dll", "kernel42.dll");
		mb.replaceAll ("ADVAPI32.dll", "ADVAPI42.dll");
		mb.saveToFile(fullFN.getAsPath());
		RETURN true;
	} else {
		printf ("Could not patch " PathCharMod " because it could not be opened\n", fullFN.getAsPath());
		RETURN false;
	}
}

bool patch(const PathChar* dir, const char* diffFN, const char* targetFN)
{
	SET_SCOPE;
	BGCPString fullDiffFN(dir, CP_SYSTEM);
	fullDiffFN.addPathSep(diffFN);

	BGCPString fullTargetFN(dir, CP_SYSTEM);
	fullTargetFN.addPathSep(targetFN);

	bool result = patch (fullDiffFN.getAsPath(), fullTargetFN.getAsPath());
	DeleteFileW (fullDiffFN.getAsPath());
	RETURN result;
}

#else // Linux

void patch42 (const char* /*target*/, const char* /*fn*/)
{
   // Just leave the files untouched ...
}
#endif

////////////////////////////////////////////////////////////////////////////////
// Build install path

/*void getInstallPath (BGCPString& path, const BGCPString& subPath)
{
   path = getDataDir();
   path.addPathSep("games");
   path.addPathSep(subPath);
}*/

BGCPString getInstallPath (const BGCPString& subPath)
{
    BGCPString result (getDataDir());
    result.addPathSep("games");
    result.addPathSep(subPath);
    return result;
}

////////////////////////////////////////////////////////////////////////////////

bool isWOW64Proc()
{
#ifdef _WIN32
   typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);

   BOOL bIsWow64 = FALSE;
   LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
        GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
  
    if (fnIsWow64Process != NULL)
    {
        if (fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
        {
            //return bIsWow64; // VS: "performance warning"
            if (bIsWow64)
               return true;
            else
               return false;
        }
    }
    return false;
#else
   return false;
#endif
}

bool hasWin16Subsystem()
{
#ifdef _WIN32
	// Win16 subsystem is not present in Win64, because a CPU in 64-Bit mode cannot run in Virtual-86 mode at the same time.
	// The windows 16-bit subsystem is provided by NTVDM.exe, so simply test for that.
	return fileExists(L"C:\\Windows\\system32\\ntvdm.exe");
#else
	return true;
#endif
}

////////////////////////////////////////////////////////////////////////////////

void appendFileList (const PathChar* source, FileCache& fc)
{
	SET_SCOPE;
	BGCPString str = "\nFile listing for ";
	str += BGCPString(source, CP_SYSTEM);
	str += BGCPString("\n");
	fc.appendStr (str.getAsPath());

	// List files
	FileList fl;
	BGCPString fp(source, CP_SYSTEM);
	fp.addPathSep("*");
	if (fl.start(fp.getAsPath())) {
		do {
			str = BGCPString(fl.getFileName(), CP_SYSTEM);
			if (fl.isDirectory())
				str += " <dir>";
			else {
				str += " ";
				str += BGCPString::fromInt (fl.getSize());
			}
			str += BGCPString("\n");
			fc.appendStr (str.getAsPath());
		} while (++fl);
	}

	// Recurse
	if (fl.start (fp.getAsPath())) {
		do {
			if (fl.isDirectory()
				&&  bgstrcmp (fl.getFileName(), ".") != 0
				&&  bgstrcmp (fl.getFileName(), "..") != 0) 
			{
				BGCPString dir(source, CP_SYSTEM);
				dir.addPathSep(BGCPString(fl.getFileName(), CP_SYSTEM));
				appendFileList(dir.getAsPath(), fc);
			}
		} while (++fl);
	}

	RETURN;
}

// Create a report of what was installed, including a list of files
bool createInstallReport(const PathChar* source, const PathChar* target, int CDType, bool appendDefaultFN)
{
	SET_SCOPE;
	BGCPString ofn(target, CP_SYSTEM);
	if (appendDefaultFN) {
		ofn.addPathSep("install-report.txt");
	}
	FileCache fc;
	fc.openReadWrite(ofn.getAsPath());

	BGCPString str = "Installed ";
	str += getCDName(CDType);
	str += "\n";
	fc.appendStr(str.getAsPath());

	str = getVersionStr();
	str += "\n";
	fc.appendStr(str.getAsPath());

	// Append OS version
	// May need to switch to GetProductInfo at some point
	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724358(v=vs.85).aspx
#ifdef _WIN32
	BGCPString winVer = "Windows ";
	OSVERSIONINFOEXW versionInfo;
	versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	if (GetVersionEx((LPOSVERSIONINFOW) (&versionInfo))) {
		winVer += BGCPString::fromInt(versionInfo.dwMajorVersion);
		winVer += ".";
		winVer += BGCPString::fromInt(versionInfo.dwMinorVersion);
		winVer += ".";
		winVer += BGCPString::fromInt(versionInfo.dwBuildNumber);
	} else {
		winVer += " (unkown version)";
	}

	if (isWOW64Proc())
		winVer += "(64 bit)\n";
	else
		winVer += "(32 bit)\n";
	fc.appendStr (winVer.getAsPath());
#endif

	// Append source and target
	if (source != NULL) {
		str = "Source: ";
		str += BGCPString(source, CP_SYSTEM);
		str += "\n";
		fc.appendStr(str.getAsPath());
	}

	if (target != NULL) {
		str = "Target: ";
		str += BGCPString(target, CP_SYSTEM);
		str += "\n";
		fc.appendStr(str.getAsPath());
	}

	if (source != NULL)
		appendFileList (source, fc);
	if (target != NULL)
		appendFileList (target, fc);
	RETURN true;
}

////////////////////////////////////////////////////////////////////////////////

const int makeBATfile_InGameDir = 1; // if not specified, uses games\bat
const int makeBATfile_Win95Compatibility = 2;
const int makeBATfile_RunAsAdmin = 4;
const int makeBATfile_256Color = 8;
const int makeBATfile_SetupIPX = 16;
const int makeBATfile_NoLFB = 32;
const int makeBATfile_cdWithQuotes = 64;

void makeBATfile (const char* BATFN, const PathChar* gameDir, 
   const char* EXEFN, const PathChar* IMGFN, int flags = 0)
{
	// Create BAT file for launching the game through DosBox
	SET_SCOPE;
	BGCPString fullBATFN;

	if ((flags & makeBATfile_InGameDir) == 0) {
		fullBATFN = getDataDir();
		fullBATFN.addPathSep("games");
		fullBATFN.addPathSep("bat");
		bgmkdir (fullBATFN.getAsPath());
	} else
		fullBATFN = BGCPString(gameDir, CP_SYSTEM);
	fullBATFN.addPathSep(BATFN);

	// Windows cannot handle Unicode .bat files?
	BGCPString bat = "@echo off\n";
	if (IMGFN != NULL) {
		if (bgstrlen(IMGFN) > 0) {
			bat += "z:\\imgmount d \"";
			BGCPString CUEFN(IMGFN, CP_SYSTEM);
			CUEFN.removeCharsFromEnd(4);
			CUEFN += ".cue";
			bool appendCUEFN = fileExists(CUEFN.getAsPath());
			if (appendCUEFN)
				bat += CUEFN;
			else
				bat += BGCPString(IMGFN, CP_SYSTEM);
			bat += "\" -t iso\n";
		}
	}

	if ((flags & makeBATfile_SetupIPX) != 0)
		bat += "ipxnet connect mech2.org 10000\n";

	BGCPString compat;
	if ((flags & makeBATfile_Win95Compatibility) > 0)
		compat += "Win95 ";
	if ((flags & makeBATfile_RunAsAdmin) > 0)
		compat += "RunAsAdmin ";
	if ((flags & makeBATfile_256Color) > 0)
		compat += "256Color ";

	if (!compat.empty()) {
		bat += "set __COMPAT_LAYER=";
		bat += compat;
		bat += "\n";
	}

	bool relPath = true;
	if (bgstrlen(gameDir) > 2)
		if (gameDir[1] == ':') 
			relPath = false;

	if ((flags & makeBATfile_NoLFB) > 0)
		bat += "NoLFB\n";

	if (relPath) {
		bat += "c:\n";
		bat += "cd \\";
		if ((flags & makeBATfile_cdWithQuotes) > 0)
			bat += "\"";
		bat += BGCPString(gameDir, CP_SYSTEM);
		if ((flags & makeBATfile_cdWithQuotes) > 0)
			bat += "\"";
		bat += "\n";
	} else {
		bat.add(&gameDir[0], CP_SYSTEM, sizeof(PathChar));
		bat += ":\ncd ";
		bat += BGCPString(gameDir+2, CP_SYSTEM);
		bat += "\n";
	}

	bat += EXEFN;
	bat += " %1\nexit\n";
	bat.saveToFile(fullBATFN.getAsPath(), 1252);
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////

void addVarKey (const BGCPString& varName, const BGCPString& varVal)
{
   BGCPString key = varName;
   key += "=";
   key += varVal;
   getConfig()->addKeyValuePair("SetShellVar", key);
}

////////////////////////////////////////////////////////////////////////////////

void addKeysForNativeGame(const char* gameName, const PathChar* gamePath, 
   const char* EXEFN, bool createNewWindow = false)
{
	Config* config = getConfig();
	config->removeGame(gameName);
	config->addKeyValuePair("GameName", gameName);
	config->addKeyValuePair("StartMethod", "RunNative");
	config->addKeyValuePair("GamePath", BGCPString(gamePath, CP_SYSTEM));
	config->addKeyValuePair("compatibility", "Win95");

	BGCPString exeFN;
	if (createNewWindow)
		exeFN = "cmd /c start ";
	exeFN += BGCPString(gamePath, CP_SYSTEM);
	exeFN.addPathSep();
	exeFN += EXEFN;
	config->addKeyValuePair("ExecutableFN", exeFN);
	config->addKeyValuePair("", "");
}

////////////////////////////////////////////////////////////////////////////////

void addKeysForDosBox(const char* gameName, const char* batFile)
{
	Config* config = getConfig();
	config->removeGame(gameName);
	config->addKeyValuePair("GameName", gameName);
	config->addKeyValuePair("StartMethod", "RunNative");

	BGCPString DosBoxPath = "%mechVMRootDir%\\DosBox";
	BGCPString DosBoxExe = DosBoxPath;
	DosBoxExe.addPathSep("dosbox.exe");
	config->addKeyValuePair("ExecutableFN", DosBoxExe);
	config->addKeyValuePair("GamePath", DosBoxPath);

	BGCPString parameters = "-conf \"";
	parameters += DosBoxPath;
	parameters += OSPathSep;
	parameters += "DosBox.conf\" -c ";
	parameters += batFile;
	config->addKeyValuePair("parameters", parameters);
	config->addKeyValuePair("", "");
}

////////////////////////////////////////////////////////////////////////////////

void addKeysForInternalSim(const BGCPString& gameName, const BGCPString& gamePathPart, 
	const BGCPString& campaignDir, bool addMercsMechs, const PathChar* imgFN)
{
	BGCPString gamePath = "%mechVMRootDir%";
	gamePath.addPathSep("games");
	gamePath.addPathSep(gamePathPart);

	Config* config = getConfig();
	config->removeGame(gameName);
	config->addKeyValuePair("GameName", gameName);
	config->addKeyValuePair("StartMethod", "InternalShell");
	config->addKeyValuePair("SetShellVar", "SimType=internal");

	BGCPString levelDir = "%mechVMRootDir%";
	levelDir.addPathSep("campaigns");
	levelDir.addPathSep(campaignDir);
	config->addKeyValuePair("SetShellVar", BGCPString("levelDir=") + levelDir);
	config->addKeyValuePair("CampaignFile", "campaign.xml");

	BGCPString archive1Path = gamePath;
	BGCPString archive2Path = gamePath;
	archive1Path.addPathSep("DATABASE.MW2");
	archive2Path.addPathSep("MW2.PRJ");

	addVarKey("GamePath", gamePath);
	config->addKeyValuePair ("archive", archive1Path);
	config->addKeyValuePair ("archive", archive2Path);

	if (addMercsMechs) {
		config->addKeyValuePair("SetShellVar", "mechlist=mw2+Commando");
		config->addKeyValuePair("SetShellVar", "configs=COM-2D Commando");
	}

	if (imgFN != nullptr) {
		config->addKeyValuePair("SetShellVar", BGCPString("iso=") + BGCPString(imgFN, CP_SYSTEM));
	}

	config->addKeyValuePair("", "");
}

////////////////////////////////////////////////////////////////////////////////

bool install_MW2_31stcc_DOS(InstallData& installData)
{
	bool DosCanRunNatively = hasWin16Subsystem();
	BGCPString gamePathPart = "mw2-31st";
	BGCPString gamePath = getInstallPath(gamePathPart);
	installData.installerWin->displayMsg ("Installing MW2: 31st Century Combat");
	bool hasDosVer = (installData.CDtype == MW2_31stcc_DOS || installData.CDtype == MW2_NetMech);
	bool hasWinVer = (installData.CDtype == MW2_31stcc_PE  || installData.CDtype == MW2_NetMech);

	// Create directories
	bgmkdir (gamePath.getAsPath());
	mkdir (gamePath, "CFGS");
	mkdir (gamePath, "GIDDI");
	mkdir (gamePath, "MEK");
	mkdir (gamePath, "SETUP");
	mkdir (gamePath, "SND");
	mkdir (gamePath, "VFX");
	mkdir (gamePath, "DEMODATA");
	mkdir (gamePath, "SMK");

	BGCPString MW2RegPath = gamePath;
	MW2RegPath.addPathSep("MW2REG.CFG");
	BGCPString MW2RegPathBak = MW2RegPath + ".bak";
	bool preserveMW2Reg = fileExists(MW2RegPath.getAsPath());
	if (preserveMW2Reg) {
		copyFile(MW2RegPath.getAsPath(), MW2RegPathBak.getAsPath());
	}

	// Copy directories
	copyDir (installData.source, "GIDDI", gamePath.getAsPath(), "GIDDI");
	copyDir (installData.source, "GIDDIDOS", gamePath.getAsPath(), "GIDDI");
	copyDir (installData.source, "GIDDIWIN", gamePath.getAsPath(), "GIDDI");
	copyDir (installData.source, "SND", gamePath.getAsPath(), "SND");
	copyDir (installData.source, "VFX", gamePath.getAsPath(), "VFX");
	copyDir (installData.source, "MECH2", gamePath.getAsPath(), "");
	copyDir (installData.source, "CFGS", gamePath.getAsPath(), "");
	copyDir (installData.source, "SMK", gamePath.getAsPath(), "SMK");

	BGCPString installPath = getDataDir();
	installPath.addPathSep("install");
	copyDir (installPath.getAsPath(), "MW2-31st-DOS", gamePath.getAsPath(), "");
	copyDir (installPath.getAsPath(), "NoLFB", gamePath.getAsPath(), "");
	copyDir (installPath.getAsPath(), "MW2-31st-DOS" OSPathSepStr "SND", gamePath.getAsPath(), "SND");

	// Copy files
	copyFile (installData.source, "vesachk.exe", gamePath.getAsPath(),"SETUP" OSPathSepStr "VESACHK.EXE");
	copyFile (installData.source, "fixboot.exe", gamePath.getAsPath(), "SETUP" OSPathSepStr "FIXBOOT.EXE");
	copyFile (installData.source, "inwin.exe", gamePath.getAsPath(), "SETUP" OSPathSepStr "INWIN.EXE");
	copyFile (installData.source, "german.lrm", gamePath.getAsPath(), "GERMAN.LRM");
	copyFile (installData.source, "readme.txt", gamePath.getAsPath(), "README.TXT");
	copyFile (installPath.getAsPath(), "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");

	if (hasDosVer && DosCanRunNatively)
		copyFile (installPath.getAsPath(), "tools" OSPathSepStr "mouse2kv.exe", gamePath.getAsPath(), "mouse2kv.exe");

	if (hasWinVer) {
		copyFiles (installData.source, "*.mw2", gamePath.getAsPath());
		copyFiles (installData.source, "*.bwd", gamePath.getAsPath());
		copyFiles (installData.source, "about.*", gamePath.getAsPath());
		copyFiles (installData.source, "tech.*", gamePath.getAsPath());
		copyFiles (installData.source, "mw2*", gamePath.getAsPath());

		copyFile (installData.source, "input.bak", gamePath.getAsPath(), "input.bak");
		copyFile (installData.source, "netmechw.dll", gamePath.getAsPath(), "netmechw.dll");
		copyFile (installData.source, "readme.txt", gamePath.getAsPath(), "readme.txt");
		copyFile (installData.source, "smackw32.dll", gamePath.getAsPath(), "smackw32.dll");
		copyFile (installData.source, "symlog.txt", gamePath.getAsPath(), "symlog.txt");
		//copyFile (source, "tmp.out", gamePath.getAsPath(), "tmp.out");
		copyFile (installData.source, "wail32.dll", gamePath.getAsPath(), "wail32.dll");
		copyFile (installData.source, "mech2.ex_", gamePath.getAsPath(), "mech2win.exe");

		BGCPString subDirMech2(installData.source, CP_SYSTEM);
		subDirMech2.addPathSep("mech2");
		copyFile (subDirMech2.getAsPath(), "gamekey.map", gamePath.getAsPath(), "GAMEKEY.MAP");
		copyFile (subDirMech2.getAsPath(), "input.map", gamePath.getAsPath(), "input.map");

		copyDir (installPath.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");

		BGCPString DirectXPath = BGCPString(installData.source, CP_SYSTEM);
		DirectXPath.addPathSep("DIRECTX");
		copyFile (DirectXPath.getAsPath(), "DPLAY.DLL", gamePath.getAsPath(), "dplay.dll");
		copyFile (DirectXPath.getAsPath(), "DSOUND.DLL", gamePath.getAsPath(), "dsound.dll");
	}

	if (preserveMW2Reg) {
		copyFile(MW2RegPathBak.getAsPath(), MW2RegPath.getAsPath());
	}

	// Create registry keys for running the DOS version natively
	#ifdef _WIN32
	if (hasDosVer && DosCanRunNatively) {
		setWin95Mode (gamePath.getAsPath(), "MW2.exe");
		setWin95Mode (gamePath.getAsPath(), "MECH2.exe");
	}
	#endif

	// Get image file name
	BGCPString imgFNStr(installData.imgFN, CP_SYSTEM);
	bool haveIMGFN = (installData.imgFN != NULL);
	if (hasDosVer && !haveIMGFN) {
		messageBox("CD Image file needed", "Please select the location of a CD "
		           "image file. This is needed to run the game in DosBox.");
		haveIMGFN = getOpenFileName(imgFNStr);
		if (!haveIMGFN)
			installData.installerWin->displayMsg ("Cannot add game to game list - missing CD image file");
	}

	// Create registry keys for the Win95 version and patch it 
	if (hasWinVer) {
		#ifdef _WIN32
		setWin95Mode (gamePath.getAsPath(), "MECH2WIN.exe", true);
		#endif
		patch42 (gamePath.getAsPath(), "MECH2WIN.EXE");
		//patch42 (gamePath.getAsPath(), "mech2.exe");
		patch42 (gamePath.getAsPath(), "mw2.dll");
		patch42 (gamePath.getAsPath(), "mw2shell.dll");
	}

	// Import mechs
	installData.installerWin->displayMsg("Installation successful, now importing mechs");
	size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);

	if (haveIMGFN) {
		// Start game in DosBox
		addKeysForDosBox("MW2: 31st Century Combat (DOS, DosBox)", "mw2");

		// Create BAT file for launching the game through DosBox
		makeBATfile ("mw2.bat", gamePathPart.getAsPath(), "mech2.exe", imgFNStr.getAsPath());

		// Use MechVM shell, but run sim through DosBox
		makeBATfile ("mw2ml.bat", gamePathPart.getAsPath(), "mw2.exe", imgFNStr.getAsPath());
	}

	#ifdef _WIN32
	if (DosCanRunNatively) {
		// Store entry to start the game natively
		makeBATfile ("_mw2.bat", gamePath.getAsPath(), "mech2.exe", NULL,
			makeBATfile_InGameDir | makeBATfile_Win95Compatibility | makeBATfile_NoLFB);
		addKeysForNativeGame("MW2: 31st Century Combat (DOS, native)",
			gamePath.getAsPath(), "_mw2.bat", true);
	}
	#endif

	// Add MechVM.cfg entries for Win95 edition
	#ifdef _WIN32
	if (hasWinVer) {
	makeBATfile ("_mw2win.bat", gamePath.getAsPath(), "mech2win.exe", NULL,
		makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
		makeBATfile_RunAsAdmin | makeBATfile_cdWithQuotes);
	addKeysForNativeGame ("MW2: 31st Century Combat (Win95, native)",
		gamePath.getAsPath(), "_mw2win.bat");
	}
	#endif
	addKeysForInternalSim("MW2: 31st Century Combat (MechVM)", gamePathPart, "mw2-31stcc", false, installData.imgFN);

	createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);
	BGCPString msg = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported. ");
	installData.installerWin->displayMsg(msg);
	return true;
}

////////////////////////////////////////////////////////////////////////////////

// Check if the file given by fn1 is encrypted. If so, decrypt it using mb2
bool xorDecrypt(const PathChar* fn1, const MemoryBlock& mb2)
{
	MemoryBlock mb1;
	bool mb1Loaded = mb1.loadFromFile(fn1);
	if (mb1Loaded) {
		char c0 = mb1.get<char>(0);
		char c1 = mb1.get<char>(1);
		if ((c0 != '#' && c0 != 'g')
		||   c1 != ' ') 
		{
			mb1.xorData(mb2);
			return mb1.saveToFile(fn1);
		} else {
			return false;
		}
	} else {
		return false;
	}
}

bool xorDecrypt(const BGCPString& mechDir, const char* subDir, const char* objFile, const MemoryBlock& mb2) 
{
	SET_SCOPE;
	BGCPString mb1fn(mechDir);
	mb1fn.addPathSep(BGCPString(subDir, CP_ASCII));
	mb1fn.addPathSep(BGCPString(objFile, CP_ASCII));
	bool result = xorDecrypt(mb1fn.getAsPath(), mb2);
	RETURN result;
}

void decryptGBLMechs(const BGCPString& gamePath)
{
	BGCPString mw2prjfn = gamePath;
	mw2prjfn.addPathSep("MW2.PRJ");
	MechWarriorIIPRJ mw2prj;
	mw2prj.open(mw2prjfn.getAsPath(), true);
	mw2prj.enterSubDir("SNDS");
	MemoryBlock sflmb;
	auto sfl = mw2prj.getFile("PETE001S.SFL", sflmb);
	BGCPString mechDir(getDataDir());
	mechDir.addPathSep("mechs");

	xorDecrypt(mechDir, "mw2+Incubus", "Incubus.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Linebacker", "Linebacker.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Phantom", "Phantom.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Annihilator", "Annihilator.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Executioner", "Executioner.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Grizzly", "Grizzly.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Stone Rhino", "Stone Rhino.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Horned Owl", "Horned Owl.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Kodiak", "Kodiak.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Hatamoto-Chi", "Hatamoto Chi.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Naga", "Naga.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Raven", "Raven.obj", sflmb);
}

bool installGBL (InstallData& installData)
{
	BGCPString gamePathPart = "mw2-gbl";
	bool DosCanRunNatively = false;
	bool hasDosVer = (installData.CDtype == MW2_GBL_DOS || installData.CDtype == MW2_GBL_Hybrid_DOS_Win);
	bool hasWinVer = (installData.CDtype == MW2_GBL_PE  || installData.CDtype == MW2_GBL_Hybrid_DOS_Win);
	if (hasDosVer)
		DosCanRunNatively = hasWin16Subsystem();
	BGCPString gamePath = getInstallPath(gamePathPart);
	installData.installerWin->displayMsg("Installing MW2: Ghost Bear's Legacy");

	// Create directories
	bgmkdir(gamePath.getAsPath());
	mkdir(gamePath, "CFGS");
	mkdir(gamePath, "GIDDI");
	mkdir(gamePath, "MEK");
	mkdir(gamePath, "SETUP");
	mkdir(gamePath, "SND");
	mkdir(gamePath, "VFX");
	mkdir(gamePath, "SMK");

	BGCPString MW2RegPath(gamePath);
	MW2RegPath.addPathSep("MW2REG.CFG");
	BGCPString MW2RegPathBak = MW2RegPath + ".bak";
	bool preserveMW2Reg = fileExists(MW2RegPath.getAsPath());
	if (preserveMW2Reg) {
		copyFile(MW2RegPath.getAsPath(), MW2RegPathBak.getAsPath());
	}

	// Copy directories
	BGCPString dataDir = getDataDir();
	const PathChar* dataDirChars = dataDir.getAsPath();
	copyDir (installData.source, "gbl", gamePath.getAsPath(), "");
	copyDir (installData.source, "giddi", gamePath.getAsPath(), "GIDDI");
	copyDir (installData.source, "snd", gamePath.getAsPath(), "SND");
	copyDir (installData.source, "vfx", gamePath.getAsPath(), "VFX");
	copyDir (installData.source, "smk", gamePath.getAsPath(), "smk");
	copyFile (dataDirChars, "install" OSPathSepStr "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");

	BGCPString installPath = getDataDir();
	installPath.addPathSep("install");

   if (hasDosVer) {
      copyDir (installPath.getAsPath(), "NoLFB", gamePath.getAsPath(), "");
      copyDir (installPath.getAsPath(), "MW2-GBL-DOS", gamePath.getAsPath(), "");
      copyDir (installPath.getAsPath(), "MW2-31ST-DOS" OSPathSepStr "SND", gamePath.getAsPath(), "SND");

      /// Copy single files
      copyFile (installData.source, "german.lrm", gamePath.getAsPath(), "GERMAN.LRM");
      copyFile (installData.source, "readme.txt", gamePath.getAsPath(), "README.TXT");
      copyFile (installData.source, "gblinfo.txt", gamePath.getAsPath(), "GBLInfo.TXT");
      copyFile (installData.source, "cfgs" OSPathSepStr "mw2.phx", gamePath.getAsPath(), "MW2.PHX");
      
      BGCPString setupPath = gamePath;
      setupPath.addPathSep("SETUP");
      copyFile (installData.source, "fixboot.exe", setupPath.getAsPath(), "FixBoot.exe");
      copyFile (installData.source, "inwin.exe", setupPath.getAsPath(), "INWIN.EXE");
      copyFile (installData.source, "mw2reg.exe", setupPath.getAsPath(), "MW2REG.EXE");
      copyFile (installData.source, "mw2reg.ini", setupPath.getAsPath(), "MW2REG.INI");
      copyFile (installData.source, "vesachk.exe", setupPath.getAsPath(), "VESACHK.EXE");

      if (DosCanRunNatively) {
         setWin95Mode (gamePath.getAsPath(), "GBL.EXE");
         copyFile (dataDirChars, "install" OSPathSepStr "tools" OSPathSepStr "mouse2kv.exe", 
            gamePath.getAsPath(), "mouse2kv.exe");
      }
   }

   if (hasWinVer) {
      copyFile (installData.source, "mw2.dll", gamePath.getAsPath(), "mw2.dll");
      copyFile (installData.source, "mw2shell.dll", gamePath.getAsPath(), "mw2shell.dll");
      copyFile (installData.source, "smackw32.dll", gamePath.getAsPath(), "smackw32.dll");
      copyFile (installData.source, "wail32.dll", gamePath.getAsPath(), "wail32.dll");
      copyFile (installData.source, "gbl" OSPathSepStr "gblwin.exe", gamePath.getAsPath(), "GBLWin.exe");

      copyDir (installPath.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");

      patch42 (gamePath.getAsPath(), "mw2.dll");
      patch42 (gamePath.getAsPath(), "mw2shell.dll");
      setWin95Mode (gamePath.getAsPath(), "GBLWin.exe");

	  BGCPString DirectXPath = BGCPString(installData.source, CP_SYSTEM);
	  DirectXPath.addPathSep("DIRECTX");
	  copyFile (DirectXPath.getAsPath(), "DPLAY.DLL", gamePath.getAsPath(), "dplay.dll");
	  copyFile (DirectXPath.getAsPath(), "DSOUND.DLL", gamePath.getAsPath(), "dsound.dll");
   }

   // Import mechs
   installData.installerWin->displayMsg("Installation successful, now importing mechs");
   size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);

   // Add entries for launching the game from MechVM
   BGCPString imgFNstr(installData.imgFN, CP_SYSTEM);
   bool haveIMGFN = (installData.imgFN != NULL);
   if (hasDosVer && !haveIMGFN) {
      messageBox("CD Image file needed", "Please select the location of a CD "
                 "image file. This is needed to run the game in DosBox.");
      if (getOpenFileName(imgFNstr))
         haveIMGFN = true;
      else
          installData.installerWin->displayMsg ("Cannot add game to game list - missing CD image file");
   }

   if (hasDosVer && haveIMGFN) {
      makeBATfile ("gbl.bat", gamePathPart.getAsPath(), "gbl.exe", 
         imgFNstr.getAsPath());
      addKeysForDosBox("MW2: Ghost Bear's Legacy (DOS, DosBox)", "gbl.bat");

      makeBATfile ("_gbldbml.bat", gamePathPart.getAsPath(), "mw2.exe", 
         imgFNstr.getAsPath());
   }

   if (DosCanRunNatively && hasDosVer) {
      makeBATfile ("_gbldos.bat", gamePath.getAsPath(), "gbl.exe", NULL, 
         makeBATfile_InGameDir | makeBATfile_Win95Compatibility | makeBATfile_NoLFB);
      addKeysForNativeGame("MW2: Ghost Bear's Legacy (DOS, native)", 
         gamePath.getAsPath(), "_gbldos.bat", true);

	#ifdef _WIN32
         makeBATfile ("_gblml.bat", gamePath.getAsPath(), "gbl.exe", NULL, 
            makeBATfile_InGameDir | makeBATfile_Win95Compatibility);
	#endif
   }

   if (hasWinVer) {
      makeBATfile ("_gblwin.bat", gamePath.getAsPath(), "gblwin.exe", NULL, 
         makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
         makeBATfile_RunAsAdmin | makeBATfile_cdWithQuotes);
	#ifdef _WIN32
      addKeysForNativeGame ("MW2: Ghost Bear's Legacy (Win95, native)",
         gamePath.getAsPath(), "_gblwin.bat");
	#endif
   }

   if (preserveMW2Reg) {
	   copyFile(MW2RegPathBak.getAsPath(), MW2RegPath.getAsPath());
   }

	createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);
	BGCPString str = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
	installData.installerWin->displayMsg(str);

	decryptGBLMechs(gamePath);
	addKeysForInternalSim("MW2: Ghost Bear's Legacy (MechVM)", gamePathPart, gamePathPart, false, installData.imgFN);

	return true;
}

////////////////////////////////////////////////////////////////////////////////

void decryptMercsMechs(const BGCPString& gamePath)
{
	BGCPString mw2prjfn = gamePath;
	mw2prjfn.addPathSep("MW2.PRJ");
	MechWarriorIIPRJ mw2prj;
	mw2prj.open(mw2prjfn.getAsPath(), true);
	mw2prj.enterSubDir("SNDS");
	MemoryBlock sflmb;
	auto sfl = mw2prj.getFile("WOL1001S.SFL", sflmb);
	BGCPString mechDir(getDataDir());
	mechDir.addPathSep("mechs");

	xorDecrypt(mechDir, "mw2+Clint", "Clint.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Commando", "Commando.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Crab", "Crab.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Hatamoto-Chi", "Hatamoto Chi.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Hunchback", "Hunchback.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Jenner", "Jenner.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Pegasus", "Pegasus.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Stalker", "Stalker.obj", sflmb);
	xorDecrypt(mechDir, "mw2+Zeus", "Zeus.obj", sflmb);
}

bool installMercs_DOS (InstallData& installData)
{
	BGCPString gamePathPart = "mw2-merx"; // mw2-merx fits the 8.3 scheme, mw2-mercs wouldn't, and that could cause problems with DosBox
	bool DosCanRunNatively = hasWin16Subsystem();
	BGCPString gamePath = getInstallPath(gamePathPart);
	installData.installerWin->displayMsg ("Installing MW2: Mercenaries (DOS)");

	// Create directories
	bgmkdir (gamePath.getAsPath());
	mkdir (gamePath, "CFGS");
	mkdir (gamePath, "GIDDI");
	mkdir (gamePath, "MEK");
	mkdir (gamePath, "SND");
	mkdir (gamePath, "VFX");
	mkdir (gamePath, "MISSIONS");
	mkdir (gamePath, "RMG");
	mkdir (gamePath, "SMK");
	mkdir (gamePath, "DLL");
	mkdir (gamePath, "keating");

	// Copy directories
	BGCPString dataDir = getDataDir();
	const PathChar* dataDirChars = dataDir.getAsPath();
	copyDir (installData.source, "giddi", gamePath.getAsPath(), "giddi");
	copyDir (installData.source, "snd", gamePath.getAsPath(), "snd");
	copyDir (installData.source, "vfx", gamePath.getAsPath(), "vfx");
	copyDir (installData.source, "missions", gamePath.getAsPath(), "missions");
	copyDir (installData.source, "rmg", gamePath.getAsPath(), "rmg");
	copyDir (installData.source, "smk", gamePath.getAsPath(), "smk");
	copyDir (installData.source, "dll", gamePath.getAsPath(), "dll");
	copyDir (installData.source, "mercs", gamePath.getAsPath(), "");
	copyDir (installData.source, "dos", gamePath.getAsPath(), "");
	copyDir (installData.source, "smk", gamePath.getAsPath(), "smk");
	copyDir (installData.source, "keating", gamePath.getAsPath(), "keating");
	copyDir (dataDirChars, "install" OSPathSepStr "MW2-Mercs-DOS", gamePath.getAsPath(), "");
	copyDir (dataDirChars, "install" OSPathSepStr "NoLFB", gamePath.getAsPath(), "");

	/// Copy single files
	//copyFile (source, "FixBoot.EXE", gamePath.getAsPath(), "SETUP\\FixBoot.exe");
	//copyFile (source, "VESACHK.EXE", gamePath.getAsPath(), "SETUP\\VESACHK.EXE");
	//copyFile (source, "GERMAN.LRM", gamePath.getAsPath(), "GERMAN.LRM");
	//copyFile (source, "README.TXT", gamePath.getAsPath(), "README.TXT");
	copyFile (dataDirChars, "install" OSPathSepStr "MW2-Mercs-DOS" OSPathSepStr "SND" OSPathSepStr "DIG.INI", 
		gamePath.getAsPath(), "SND" OSPathSepStr "DIG.INI");
	copyFile (dataDirChars, "install" OSPathSepStr "tools" OSPathSepStr "mouse2kv.exe", gamePath.getAsPath(), "mouse2kv.exe");
	copyFile (dataDirChars, "install" OSPathSepStr "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");

	BGCPString DirectXPath = BGCPString(installData.source, CP_SYSTEM);
	DirectXPath.addPathSep("DIRECTX");
	copyFile (DirectXPath.getAsPath(), "DPLAY.DLL", gamePath.getAsPath(), "dplay.dll");
	copyFile (DirectXPath.getAsPath(), "DSOUND.DLL", gamePath.getAsPath(), "dsound.dll");

	// Install compatibility layer
	BGCPString installPath = getDataDir();
	installPath.addPathSep("install");
	copyDir (installPath.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");
	patch42 (gamePath.getAsPath(), "MERCSWIN.EXE");
	patch42 (gamePath.getAsPath(), "MW2.DLL");
	patch42 (gamePath.getAsPath(), "MW2SHELL.DLL");

	// Create registry key
	#ifdef _WIN32
	setWin95Mode (gamePath.getAsPath(), "MERCS.EXE");
	setWin95Mode (gamePath.getAsPath(), "MERCSWIN.EXE");
	#endif

	// Add MercNet
	BGCPString imgFNstr(installData.imgFN, CP_SYSTEM);
	bool haveIMGFN = (installData.imgFN != NULL);
	if (!haveIMGFN) {
		messageBox("CD Image file needed", "Please select the location of a CD "
			"image file. This is needed to run MW2:Mercenaries in DosBox and host "
			"games on MercNet.");
		haveIMGFN = getOpenFileName(imgFNstr);
	}

	#ifdef _WIN32
	if (DosCanRunNatively) {
		makeBATfile ("_mercsdos.bat", gamePath.getAsPath(), "mercs.exe", NULL, 
			makeBATfile_InGameDir | makeBATfile_Win95Compatibility | makeBATfile_NoLFB);
		addKeysForNativeGame ("MW2: Mercenaries (DOS, native)",
			gamePath.getAsPath(), "_mercsdos.bat", true);
	}
	makeBATfile ("_mercswin.bat", gamePath.getAsPath(), "mercswin.exe", NULL, 
		makeBATfile_InGameDir | makeBATfile_Win95Compatibility | makeBATfile_RunAsAdmin | makeBATfile_cdWithQuotes);
	addKeysForNativeGame ("MW2: Mercenaries (Win95, native)",
		gamePath.getAsPath(), "_mercswin.bat");

	makeBATfile ("mercnet.bat", gamePathPart.getAsPath(), "mercnet.exe", 
		imgFNstr.getAsPath(), makeBATfile_SetupIPX);
	addKeysForDosBox("MW2: MercNet (DOS, DosBox)", "mercnet");
	#endif

	addKeysForInternalSim("MW2: Mercenaries (MechVM)", gamePathPart, "mw2-mercs", true, installData.imgFN);

	BGCPString msg;
	if (haveIMGFN) {
		makeBATfile ("mercs.bat", gamePathPart.getAsPath(), "mercs.exe", 
			imgFNstr.getAsPath());
		addKeysForDosBox("MW2: Mercenaries (DOS, DosBox)", "mercs.bat");
		makeBATfile ("_mercsml.bat", gamePathPart.getAsPath(), "mw2.exe", 
			imgFNstr.getAsPath());
	} else
		msg += "Cannot add DosBox game to game list - no CD image file. ";

	// Import mechs
	installData.installerWin->displayMsg("Installation successful, now importing mechs");
	size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);
	decryptMercsMechs(gamePath);
	msg += BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported. ");

	createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);
	installData.installerWin->displayMsg(msg);

	return true;
}

////////////////////////////////////////////////////////////////////////////////
// Install MW2 31st Century Combat, Battlepack edition

bool install_MW2_BattlePack (InstallData& installData, const char* subDir)
{
	BGCPString gamePath = getInstallPath(subDir);
	installData.installerWin->displayMsg ("Installing MW2: 31st Century Combat (Battlepack)");

	// Create directories
	bgmkdir (gamePath.getAsPath());
	mkdir (gamePath, "CFGS");
	mkdir (gamePath, "GIDDI");
	mkdir (gamePath, "SMK");
	mkdir (gamePath, "VFX");
	mkdir (gamePath, "Windll");

	// Copy files
	BGCPString dataDir = getDataDir();
	const PathChar* dataDirChars = dataDir.getAsPath();
	copyDir(installData.source, "cfgs", gamePath.getAsPath(), "cfgs");
	copyDir(installData.source, "giddi", gamePath.getAsPath(), "giddi");
	copyDir(installData.source, "smk", gamePath.getAsPath(), "smk");
	copyDir(installData.source, "VFX", gamePath.getAsPath(), "VFX");
	copyDir(installData.source, "Windll", gamePath.getAsPath(), "windll");
	copyDir(dataDirChars, "INSTALL" OSPathSepStr "MW2-Dll42", gamePath.getAsPath(), "");

	copyFile(installData.source, "HELP.EXE", gamePath.getAsPath(), "HELP.EXE");
	copyFile(installData.source, "README.TXT", gamePath.getAsPath(), "README.TXT");
	copyFiles(installData.source, "*.CNT", gamePath.getAsPath());
	copyFiles(installData.source, "*.HLP", gamePath.getAsPath());
	copyFile(dataDirChars, "install" OSPathSepStr "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");

	if (installData.CDtype == MW2_31stcc_BP) {
		copyDir(installData.source, "MW2", gamePath.getAsPath(), "");
		copyFile(installData.source, "ARCHJF.MW2", gamePath.getAsPath(), "ARCHJF.MW2");
		copyFile(installData.source, "ARCHWO.MW2", gamePath.getAsPath(), "ARCHWO.MW2");
		mkdir(gamePath, "Missions");
		copyDir(installData.source, "Missions", gamePath.getAsPath(), "Missions");
		copyDir(dataDirChars, "install" OSPathSepStr "MW2-31stcc-bp", gamePath.getAsPath(), "");

		setWin95Mode (gamePath.getAsPath(), "MW2WIN.exe", true);

		makeBATfile("_mw2bp.bat", gamePath.getAsPath(), "mw2win.exe", NULL,
			makeBATfile_InGameDir | makeBATfile_Win95Compatibility 
			| makeBATfile_RunAsAdmin | makeBATfile_cdWithQuotes);
		getConfig()->addKeyValuePair("GameName", "MW2: 31st Century Combat (BattlePack)");
		getConfig()->addKeyValuePair("StartMethod", "RunNative");
		BGCPString exeFN = gamePath;
		exeFN.addPathSep("_mw2bp.bat");
		getConfig()->addKeyValuePair("compatibility", "Win95");
		getConfig()->addKeyValuePair("GamePath", gamePath);
		getConfig()->addKeyValuePair("ExecutableFN", exeFN);
		getConfig()->addKeyValuePair("", "");
	} else {
		copyDir(installData.source, "GBL", gamePath.getAsPath(), "");
		mkdir(gamePath, "giddiwin");
		copyDir(installData.source, "giddiwin", gamePath.getAsPath(), "giddiwin");
		setWin95Mode(gamePath.getAsPath(), "GBLWIN.exe", true);

		makeBATfile("_gblbp.bat", gamePath.getAsPath(), "gblwin.exe", NULL,
			makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
			makeBATfile_RunAsAdmin | makeBATfile_cdWithQuotes);

		getConfig()->addKeyValuePair("GameName", "MW2: Ghost Bear's Legacy (BattlePack)");
		getConfig()->addKeyValuePair("StartMethod", "RunNative");
		BGCPString exeFN = gamePath;
		exeFN.addPathSep("_gblbp.bat");
		getConfig()->addKeyValuePair("compatibility", "Win95");
		getConfig()->addKeyValuePair("GamePath", BGCPString(gamePath.getAsPath(), CP_SYSTEM));
		getConfig()->addKeyValuePair("ExecutableFN", BGCPString(exeFN.getAsPath(), CP_SYSTEM));
		getConfig()->addKeyValuePair("", "");
	}

	decryptGBLMechs(gamePath);

	patch42(gamePath.getAsPath(), "MW2.DLL");
	patch42(gamePath.getAsPath(), "battle.DLL");
	patch42(gamePath.getAsPath(), "MW2SHELL.DLL");
	patch42(gamePath.getAsPath(), "NETSHELL.DLL");
	createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);
	installData.installerWin->displayMsg("Installation successful");
	return true;
}

////////////////////////////////////////////////////////////////////////////////

// Install 16-bit, hardware-accelerated version of Titanium 31st CC
bool installMW2_Titan_16bit (InstallData& installData)
{
   BGCPString gamePathPart = "mw2-31stcc-tt";
   BGCPString gamePath = getInstallPath(gamePathPart);

   installData.installerWin->displayMsg ("Installing MW2: 31st Century Combat (Titanium Trilogy)");

   // Create directories
   bgmkdir (gamePath.getAsPath());
   mkdir (gamePath, "CFGS");
   mkdir (gamePath, "GIDDI");
   mkdir (gamePath, "GIDDIWIN");
   mkdir (gamePath, "MEK");
   mkdir (gamePath, "Missions");
   mkdir (gamePath, "SMK");
   mkdir (gamePath, "plugin");

   // Copy files
   BGCPString installDir(installData.source, CP_SYSTEM);
   installDir.addPathSep("Install");

   BGCPString dataDir = installDir;
   dataDir.addPathSep("data");
   copyDir (dataDir.getAsPath(), "", gamePath.getAsPath(), "");
   copyDir (dataDir.getAsPath(), "cfgs", gamePath.getAsPath(), "CFGS");
   copyDir (dataDir.getAsPath(), "giddi", gamePath.getAsPath(), "GIDDI");
   copyDir (dataDir.getAsPath(), "giddiwin", gamePath.getAsPath(), "GIDDIWIN");
   copyDir (installData.source, "Smk", gamePath.getAsPath(), "SMK");

   BGCPString dir16 = installDir;
   dir16.addPathSep("16bit");
   copyDir (dir16.getAsPath(), "", gamePath.getAsPath(), "");
   copyDir (dir16.getAsPath(), "plugin", gamePath.getAsPath(), "plugin");
   copyDir (dir16.getAsPath(), "Missions", gamePath.getAsPath(), "Missions");

   #ifdef _WIN32
   BGCPString additionalInstallFilesPath = getDataDir();
   additionalInstallFilesPath.addPathSep("install");
   copyDir (additionalInstallFilesPath.getAsPath(), "MW2-31st-Titan", gamePath.getAsPath(), "");
   copyDir (additionalInstallFilesPath.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");
   copyFile (additionalInstallFilesPath.getAsPath(), "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");
   copyFile (additionalInstallFilesPath.getAsPath(), "tools" OSPathSepStr "_titanRes3.exe", gamePath.getAsPath(), "_titanRes3.exe");

   // Patch mw2shell.dll
   patch(gamePath.getAsPath(), "MW2shell.dll.diff", "MW2shell.dll");
   patch42(gamePath.getAsPath(), "NETMECH.EXE");

   // Create registry key
   setWin95Mode (gamePath.getAsPath(), "MW2WIN.exe");
   #else
   // MW2.PRJ has capital letters in Titanium Trilogy ... change to lower case
   // for compatibility between Linux and the DOS CDs
   BGCPString FN1 = gamePath;
   FN1.addPathSep("MW2.PRJ");
   BGCPString FN2 = gamePath.getAsPath();
   FN2.addPathSep("mw2.prj");
   rename (FN1.getAsPath(), FN2.getAsPath());
   #endif

   // Import mechs
   installData.installerWin->displayMsg("Installation successful, now importing mechs");
   size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);
   BGCPString str = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
   installData.installerWin->displayMsg(str);

   // Add entries for launching the game from MechVM
   #ifdef _WIN32
   makeBATfile("_mw2-31stcc.bat", gamePath.getAsPath(), "mw2win", NULL,
      makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
      makeBATfile_RunAsAdmin | makeBATfile_256Color | makeBATfile_cdWithQuotes);
   BGCPString exeFN = gamePath;
   exeFN.addPathSep("_mw2-31stcc.bat");
   addKeysForNativeGame ("MW2: 31st Century Combat (Titanium Trilogy)",
      gamePath.getAsPath(), "_mw2-31stcc.bat");
	#endif

   addKeysForInternalSim("MW2: 31st Century Combat (MechVM)", gamePathPart, "mw2-31stcc", false, installData.imgFN);

   createInstallReport(installData.source, gamePath.getAsPath(), MW2_31stcc_TITAN);
   return true;
}

// Install 8-bit, software-accelerated version (Battlepack)
bool installMW2_Titan_8bit (InstallData& installData)
{
   BGCPString gamePath;
   if (installData.CDtype == MW2_31stcc_TT_BP) {
      gamePath = getInstallPath("mw2-31stcc-bp");
      installData.installerWin->displayMsg ("Installing MW2: 31st Century Combat (Battlepack/Titanium 8-bit)");
   } else if (installData.CDtype == MW2_GBL_TT_BP) {
      gamePath = getInstallPath("mw2-gbl-bp");
      installData.installerWin->displayMsg ("Installing MW2: Ghost Bear's Legacy (Battlepack/Titanium 8-bit)");
   } else {
      gamePath = getInstallPath("mw2-mercs-bp");
      installData.installerWin->displayMsg ("Installing MW2: Mercenaries (Battlepack/Titanium 8-bit)");
   }

   // Create directories
   bgmkdir (gamePath.getAsPath());
   mkdir (gamePath, "CFGS");
   mkdir (gamePath, "GIDDI");
   mkdir (gamePath, "GIDDIWIN");
   mkdir (gamePath, "help");
   mkdir (gamePath, "keating");
   mkdir (gamePath, "Missions");
   mkdir (gamePath, "SMK");
   mkdir (gamePath, "splash");
   mkdir (gamePath, "Windll");

   // Copy files
   BGCPString dataDir = getDataDir();
   const PathChar* dataDirChars = dataDir.getAsPath();
   copyDir (installData.source, "install" OSPathSepStr "data", gamePath.getAsPath(), "");
   copyDir (installData.source, "Install" OSPathSepStr "data" OSPathSepStr "cfgs", gamePath.getAsPath(), "cfgs");
   copyDir (installData.source, "Install" OSPathSepStr "data" OSPathSepStr "giddi", gamePath.getAsPath(), "giddi");
   copyDir (installData.source, "Install" OSPathSepStr "data" OSPathSepStr "giddiwin", gamePath.getAsPath(), "giddiwin");
   copyDir (installData.source, "install" OSPathSepStr "data" OSPathSepStr "smk", gamePath.getAsPath(), "smk");
   copyDir (installData.source, "install" OSPathSepStr "8bit", gamePath.getAsPath(), "");
   copyDir (installData.source, "help", gamePath.getAsPath(), "help");
   copyDir (installData.source, "keating", gamePath.getAsPath(), "keating");
   copyDir (installData.source, "Install" OSPathSepStr "8bit" OSPathSepStr "Missions", gamePath.getAsPath(), "Missions");
   copyDir (installData.source, "splash", gamePath.getAsPath(), "splash");
   copyDir (installData.source, "windll", gamePath.getAsPath(), "windll");
   copyDir (dataDirChars, "install" OSPathSepStr "MW2-31stcc-bp", gamePath.getAsPath(), "");
   copyDir (dataDirChars, "INSTALL" OSPathSepStr "MW2-Dll42", gamePath.getAsPath(), "");

   if (installData.CDtype == MW2_Mercs_TT_BP) {
      mkdir (gamePath, "rmg");
      copyDir (installData.source, "Install" OSPathSepStr "data" OSPathSepStr "rmg", gamePath.getAsPath(), "rmg");
   }

   // Mech.ico, copied from H:\Install\data, must be deleted, otherwise the game won't run
   const char* exeFN;
   if (installData.CDtype == MW2_31stcc_TT_BP) {
      exeFN = "MW2WIN.exe";
   } else if (installData.CDtype == MW2_GBL_TT_BP) {
      exeFN = "GBLWIN.exe";
   } else {
      exeFN = "MERCSWIN.exe";
   }
   BGCPString exePath = gamePath;
   exePath.addPathSep(exeFN);

   #ifdef _WIN32
   BGCPString mech2_ico_path = gamePath;
   mech2_ico_path.addPathSep("Mech.ico");
   bgDeleteFile(mech2_ico_path.getAsPath());

   // Create registry key
   setWin95Mode (gamePath.getAsPath(), exeFN, true);
   #endif

   patch42(gamePath.getAsPath(), "MW2.DLL");
   patch42(gamePath.getAsPath(), "battle.DLL");
   patch42(gamePath.getAsPath(), "MW2SHELL.DLL");
   patch42(gamePath.getAsPath(), "NETSHELL.DLL");

   // Import mechs
   installData.installerWin->displayMsg("Installation successful, now importing mechs");
   size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);

   // Add entries for launching the game from MechVM
   makeBATfile("_mw2.bat", gamePath.getAsPath(), exeFN, NULL,
      makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
      makeBATfile_RunAsAdmin | makeBATfile_256Color | makeBATfile_cdWithQuotes);

   if (installData.CDtype == MW2_31stcc_TT_BP) {
      addKeysForNativeGame ("MW2: 31st Century Combat (Battlepack/Titanium 8-bit)",
         gamePath.getAsPath(), "_mw2.bat");
   } else if (installData.CDtype == MW2_GBL_TT_BP) {
      addKeysForNativeGame ("MW2: Ghost Bear's Legacy (Battlepack/Titanium 8-bit)",
         gamePath.getAsPath(), "_mw2.bat");
      decryptGBLMechs(gamePath);
   } else {
      addKeysForNativeGame ("MW2: Mercenaries (Battlepack/Titanium 8-bit)",
         gamePath.getAsPath(), "_mw2.bat");
   }

   createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);

   BGCPString str = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
   installData.installerWin->displayMsg(str);

   return true;
}

////////////////////////////////////////////////////////////////////////////////

bool installGBL_Titan (InstallData& installData)
{
	BGCPString gamePathPart = "mw2-gbl-tt";
	BGCPString gamePath = getInstallPath(gamePathPart);
	installData.installerWin->displayMsg ("Installing MW2: Ghost Bear's Legacy (Titanium Trilogy)");

	// Create directories
	bgmkdir (gamePath.getAsPath());
	mkdir (gamePath, "CFGS");
	mkdir (gamePath, "GIDDI");
	mkdir (gamePath, "GIDDIWIN");
	mkdir (gamePath, "MEK");
	mkdir (gamePath, "SMK");
	mkdir (gamePath, "plugin");

	// Copy files
	BGCPString installDir(installData.source, CP_SYSTEM);
	installDir.addPathSep("Install");

	BGCPString dataDir = installDir;
	dataDir.addPathSep("data");
	copyDir (dataDir.getAsPath(), "", gamePath.getAsPath(), "");
	copyDir (dataDir.getAsPath(), "cfgs", gamePath.getAsPath(), "CFGS");
	copyDir (dataDir.getAsPath(), "giddi", gamePath.getAsPath(), "GIDDI");
	copyDir (dataDir.getAsPath(), "giddiwin", gamePath.getAsPath(), "GIDDIWIN");
	copyDir (installData.source, "smk", gamePath.getAsPath(), "SMK");
	copyDir (dataDir.getAsPath(), "smk", gamePath.getAsPath(), "SMK");

	BGCPString dir16 = installDir;
	dir16.addPathSep("16bit");
	copyDir (dir16.getAsPath(), "", gamePath.getAsPath(), "");
	copyDir (dir16.getAsPath(), "plugin", gamePath.getAsPath(), "plugin");

	BGCPString additionalFilesDir = getDataDir();
	additionalFilesDir.addPathSep("install");
	copyDir (additionalFilesDir.getAsPath(), "MW2-GBL-Titan", gamePath.getAsPath(), "");
	copyDir (additionalFilesDir.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");
	copyFile (additionalFilesDir.getAsPath(), "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");
	copyFile (additionalFilesDir.getAsPath(), "tools" OSPathSepStr "_titanRes3.exe", gamePath.getAsPath(), "_titanRes3.exe");

    // Create registry key
    //setWin95Mode (gamePath.getAsPath(), "gblwin.exe");

    // Patch mw2shell.dll
    #ifdef _WIN32
    patch(gamePath.getAsPath(), "MW2shell.dll.diff", "MW2shell.dll");
    patch(gamePath.getAsPath(), "MercsW.dll.diff", "MercsW.dll");
    #else
    // MW2.PRJ has capital letters in Titanium Trilogy ... for compatibility
    // Linux and the DOS CDs, change to lower case
    BGCPString FN1 = gamePath.getAsPath();
    FN1.addPathSep("MW2.PRJ");
    BGCPString FN2 = gamePath.getAsPath();
    FN2.addPathSep("mw2.prj");
    rename (FN1.getAsPath(), FN2.getAsPath());
    #endif

    // Import mechs
    installData.installerWin->displayMsg("Installation successful, now importing mechs");
    size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);

	// Create registry key
	// Seems to be required despite setting compatibility in BAT file, below
	setWin95Mode (gamePath.getAsPath(), "gblwin.exe");

	// Add entries for launching the game from MechVM
	makeBATfile("_mw2-gbl.bat", gamePath.getAsPath(), "gblwin", NULL,
		makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
		makeBATfile_RunAsAdmin | makeBATfile_256Color | makeBATfile_cdWithQuotes);
	BGCPString exeFN = gamePath;
	exeFN.addPathSep("_mw2-gbl.bat");
	addKeysForNativeGame ("MW2: Ghost Bear's Legacy (Titanium Trilogy)",
		gamePath.getAsPath(), "_mw2-gbl.bat");
	addKeysForInternalSim("MW2: Ghost Bear's Legacy (MechVM)", gamePathPart, "mw2-gbl", false, installData.imgFN);

	decryptGBLMechs(gamePath);
	decryptMercsMechs(gamePath);
	createInstallReport(installData.source, gamePath.getAsPath(), MW2_GBL_TITAN);
	BGCPString msg = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
	installData.installerWin->displayMsg(msg);
	return true;
}

////////////////////////////////////////////////////////////////////////////////

bool installMercs_Titan (InstallData& installData)
{
	BGCPString gamePathPart = "mw2-mercs-tt"; 
	BGCPString gamePath = getInstallPath(gamePathPart);
	installData.installerWin->displayMsg ("Installing MW2: Mercenaries (Titanium Trilogy)");

	// Create directories
	bgmkdir (gamePath.getAsPath());
	mkdir (gamePath, "CFGS");
	mkdir (gamePath, "GIDDI");
	mkdir (gamePath, "GIDDIWIN");
	mkdir (gamePath, "MEK");
	mkdir (gamePath, "SMK");
	mkdir (gamePath, "plugin");
	mkdir (gamePath, "RMG");
	mkdir (gamePath, "Missions");

	// Copy files
	BGCPString installDir(installData.source, CP_SYSTEM);
	installDir.addPathSep("Install");

	BGCPString dataDir = installDir;
	dataDir.addPathSep("data");
	copyDir (dataDir.getAsPath(), "", gamePath.getAsPath(), "");
	copyDir (dataDir.getAsPath(), "cfgs", gamePath.getAsPath(), "CFGS");
	copyDir (dataDir.getAsPath(), "giddi", gamePath.getAsPath(), "GIDDI");
	copyDir (dataDir.getAsPath(), "giddiwin", gamePath.getAsPath(), "GIDDIWIN");
	copyDir (dataDir.getAsPath(), "RMG", gamePath.getAsPath(), "RMG");
	copyDir (dataDir.getAsPath(), "smk", gamePath.getAsPath(), "SMK");
	copyDir (installData.source, "Smk", gamePath.getAsPath(), "SMK");

	BGCPString dir16 = installDir;
	dir16.addPathSep("16bit");
	copyDir (dir16.getAsPath(), "", gamePath.getAsPath(), "");
	copyDir (dir16.getAsPath(), "plugin", gamePath.getAsPath(), "plugin");
	copyDir (dir16.getAsPath(), "Missions", gamePath.getAsPath(), "Missions");

	BGCPString additionalInstallFilesPath = getDataDir();
	additionalInstallFilesPath.addPathSep("install");
	copyDir (additionalInstallFilesPath.getAsPath(), "MW2-mercs-Titan", gamePath.getAsPath(), "");
	copyDir (additionalInstallFilesPath.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");
	copyFile (additionalInstallFilesPath.getAsPath(), "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");
	copyFile (additionalInstallFilesPath.getAsPath(), "tools" OSPathSepStr "_titanRes3.exe", gamePath.getAsPath(), "_titanRes3.exe");

	// Create registry key
	setWin95Mode (gamePath.getAsPath(), "MercsWin.exe");

	// Patch mw2shell.dll
	#ifdef _WIN32
	patch(gamePath.getAsPath(), "MW2shell.dll.diff", "MW2shell.dll");
	patch(gamePath.getAsPath(), "MercsW.dll.diff", "MercsW.dll");
	#else
	// MW2.PRJ has capital letters in Titanium Trilogy ... for compatibility
	// Linux and the DOS CDs
	BGCPString FN1 = gamePath.getAsPath();
	FN1.addPathSep("MW2.PRJ");
	BGCPString FN2 = gamePath.getAsPath();
	FN2.addPathSep("mw2.prj");
	rename (FN1.getAsPath(), FN2.getAsPath());
	#endif
	addKeysForInternalSim("MW2: Mercenaries (MechVM)", gamePathPart, "mw2-mercs", true, installData.imgFN);

	// Import mechs
	installData.installerWin->displayMsg("Installation successful, now importing mechs");
	size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);
	BGCPString str = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
	installData.installerWin->displayMsg(str);

	#ifdef _WIN32
	makeBATfile("_mw2-mercs.bat", gamePath.getAsPath(), "mercswin.exe", NULL,
		makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
		makeBATfile_RunAsAdmin | makeBATfile_256Color | makeBATfile_cdWithQuotes);

	BGCPString exeFN = gamePath;
	exeFN.addPathSep("_mw2-mercs.bat");
	addKeysForNativeGame ("MW2: Mercenaries (Titanium Trilogy)", gamePath.getAsPath(), "_mw2-mercs.bat");
	#endif

	decryptGBLMechs(gamePath);
	decryptMercsMechs(gamePath);
	createInstallReport(installData.source, gamePath.getAsPath(), MW2_Mercs_TITAN);
	return true;
}

////////////////////////////////////////////////////////////////////////////////
// Single installer for MW2:31stCC ATI, Matrox and 3DFX editions
// (ATI: untested)

bool installMW2_31stCC_3D_enhanced (InstallData& installData)
{
	SET_SCOPE;
	BGCPString gamePathPart;
	const char* editionName;

	switch (installData.CDtype) {
	case MW2_31stcc_ATI:
		gamePathPart = "mw2-31stcc-ati";
		editionName = "MW2: 31st Century Combat (ATI edition)";
		break;
	case MW2_31stcc_Matrox:
		gamePathPart = "mw2-31stcc-matrox";
		editionName = "MW2: 31st Century Combat (Matrox edition)";
		break;
	case MW2_31stcc_3DFX:
		gamePathPart = "mw2-31stcc-3dfx";
		editionName = "MW2: 31st Century Combat (3DFX edition)";
		break;
	default:
		RETURN false;
	}

	BGCPString msg = BGCPString("Installing ") + BGCPString(editionName);
	installData.installerWin->displayMsg (msg);
	BGCPString gamePath = getInstallPath(gamePathPart);

	bgmkdir(gamePath.getAsPath());
	mkdir(gamePath, "GIDDI");
	mkdir(gamePath, "MEK");
	mkdir(gamePath, "SMK");

	copyFiles(installData.source, "*.MW2", gamePath.getAsPath());
	copyFiles(installData.source, "*.bwd", gamePath.getAsPath());
	copyFiles(installData.source, "about.*", gamePath.getAsPath());
	copyFiles(installData.source, "tech.*", gamePath.getAsPath());
	copyFiles(installData.source, "MW2*", gamePath.getAsPath());

	BGCPString additionalFilesDir = getDataDir();
	additionalFilesDir.addPathSep("INSTALL");
	copyDir (installData.source, "SMK", gamePath.getAsPath(), "SMK");
	copyDir (installData.source, "GIDDI", gamePath.getAsPath(), "GIDDI");
	copyDir (additionalFilesDir.getAsPath(), "MW2-Dll42", gamePath.getAsPath(), "");
	copyDir (additionalFilesDir.getAsPath(), "MW2-31stcc-3dfx", gamePath.getAsPath(), "");
	copyFile (additionalFilesDir.getAsPath(), "tools" OSPathSepStr "_farPatcher.exe", gamePath.getAsPath(), "_farPatcher.exe");

	BGCPString DirectXPath = BGCPString(installData.source, CP_SYSTEM);
	DirectXPath.addPathSep("DIRECTX");
	copyFile (DirectXPath.getAsPath(), "DPLAY.DLL", gamePath.getAsPath(), "dplay.dll");
	copyFile (DirectXPath.getAsPath(), "DSOUND.DLL", gamePath.getAsPath(), "dsound.dll");

	copyFile (installData.source, "mech2.ex_", gamePath.getAsPath(), "Mech2.exe");
	copyFile (installData.source, "mw2.prj", gamePath.getAsPath(), "mw2.prj");
	copyFile (installData.source, "mw2.dll", gamePath.getAsPath(), "MW2.DLL");
	//copyFile (installData.source, "mw2shell.dll", gamePath.getAsPath(), "MW2SHELL.DLL"); // use the patched version instead (works with 3dfx)
	copyFile (installData.source, "smackw32.dll", gamePath.getAsPath(), "SMACKW32.DLL");
	copyFile (installData.source, "netmechw.dll", gamePath.getAsPath(), "NETMECHW.DLL");
	copyFile (installData.source, "wail32.dll", gamePath.getAsPath(), "WAIL32.DLL");
	copyFile (installData.source, "skygnd.par", gamePath.getAsPath(), "SKYGND.PAR");
	copyFile (installData.source, "gamekey.map", gamePath.getAsPath(), "GAMEKEY.MAP");
	copyFile (installData.source, "input.map", gamePath.getAsPath(), "input.map");
	copyFile (installData.source, "input.bak", gamePath.getAsPath(), "input.bak");
	copyFile (installData.source, "readme.txt", gamePath.getAsPath(), "readme.txt");
	copyFile (installData.source, "symlog.txt", gamePath.getAsPath(), "symlog.txt");

	if (installData.CDtype == MW2_31stcc_Matrox) {
		copyFile (installData.source, "lod.par", gamePath.getAsPath(), "lod.par");
		copyFile (installData.source, "mystique.par", gamePath.getAsPath(), "mystique.par");
	}

	// Create registry key
	setWin95Mode (gamePath.getAsPath(), "mech2.exe");

	patch42 (gamePath.getAsPath(), "MECH2.EXE");
	patch42 (gamePath.getAsPath(), "MW2.DLL");
	//patch42 (gamePath.getAsPath(), "MW2SHELL.DLL"); // already patched ...
	patch42 (gamePath.getAsPath(), "NETMECHW.DLL");

	// Import mechs
	installData.installerWin->displayMsg("Installation successful, now importing mechs");
	size_t importedMechs = importMW2Files (gamePath.getAsPath(), installData.CDtype);
	BGCPString str = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
	installData.installerWin->displayMsg(str);

	createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);

#ifdef _WIN32
	makeBATfile("_mw2.bat", gamePath.getAsPath(), "mech2.exe", NULL,
		makeBATfile_InGameDir | makeBATfile_Win95Compatibility | 
		makeBATfile_RunAsAdmin | makeBATfile_256Color);
	addKeysForNativeGame (editionName, gamePath.getAsPath(), "_mw2.bat");
#endif

	addKeysForInternalSim("MW2: 31st Century Combat (MechVM)", gamePathPart, "mw2-31stcc", false, installData.imgFN);

	installData.installerWin->displayMsg("Installation complete");
	RETURN true;
}

////////////////////////////////////////////////////////////////////////////////
// Install NetMech - requires the NetMech plugin

#ifdef _WIN32
bool installNetMech(Installers* installerWin)
{
	SET_SCOPE;
	BGCPString source;
	if (!getOpenFileName(source)) {
		installerWin->displayMsg("Installation aborted");
		RETURN false;
	}

	BGCPString gamePath = getInstallPath("NetMech");
	BGCPString cmdLine (getDataDir());
	cmdLine.addPathSep();
	cmdLine += "unzip.exe -o ";
	cmdLine += BGCPString(source.getAsPath(), CP_SYSTEM);

	// Create process for decompression
	// Required files are decompressed exactly to where they need to be ...
	bgmkdir (gamePath.getAsPath());
	BGProcess process;
	if (!process.run(gamePath, cmdLine)) {
		COUT << "Failed to run process: ";
		process.getErrors().print();
	}

	// Import mechs
	installerWin->displayMsg("Installation successful, now importing mechs");
	size_t importedMechs = importMW2Files (gamePath.getAsPath(), MW2_NetMechInstall);
	BGCPString str = BGCPString("Installation complete, ") + BGCPString::fromInt(importedMechs) + BGCPString(" mechs imported");
	installerWin->displayMsg(str);

	makeBATfile("netmech.bat", L"NetMech", "netmech.exe", NULL, 0);
	addKeysForDosBox("NetMech", "netmech.bat");
	createInstallReport(source.getAsPath(), gamePath.getAsPath(), MW2_Netmech_InstallPackage);

	RETURN true;
}
#endif

////////////////////////////////////////////////////////////////////////////////

#ifdef _WIN32
void printRegError (const wchar_t* keyName, LONG opRes)
{
   if (opRes != ERROR_SUCCESS) {
      BGCPString str = "Error while opening/creating registry key ";
      str += BGCPString(keyName, CP_UTF16_LE);
      str += "\nWindows says: ";
      str += BGCPString::getOSError(opRes);
      str.printLn();
   } else
      wprintf (L"No problem creating key %s\n", keyName);
}

bool installMW3 (InstallData& installData)
{
	SET_SCOPE;
	BGCPString gamePath = getInstallPath ("mw3");
	if (installData.CDtype == MW3_PM_CD) {
		gamePath = getInstallPath ("mw3-pm");
	}
	bgmkdir (gamePath.getAsPath());

	BGCPString cmdLine (getDataDir());
	cmdLine.addPathSep();
	cmdLine += "i5comp" OSPathSepStr "i5comp.exe x ";
	cmdLine += BGCPString(installData.source, CP_SYSTEM);
	cmdLine.addPathSep();
	cmdLine += "data1.cab";

	BGProcess process;
	if (!process.run(gamePath, cmdLine)) {
		installData.installerWin->displayMsg ("Installing MW3 failed: Failed to start decompressor\n");
		process.getErrors().print();
		RETURN false;
	}

	printf ("Decompressor ended with code %i\n", process.getExitCode());

	BGCPString importXML = getDataDir();
	BGCPString str = "Importing from MechWarrior 3";
	bool piratesMoon = false;

	if (installData.CDtype == MW3_PM_CD) {
		str += " - Pirate's Moon";
		piratesMoon = true;
		importXML.addPathSep("mw3-mechs-pm.xml");
	} else {
		importXML.addPathSep("mw3-mechs.xml");
	}

	SET_OP;
	installData.installerWin->displayMsg (str);
	BGCPString mechDir(getDataDir());
	mechDir.addPathSep("mechs");
	MW3MechImporter mw3Importer(piratesMoon);
	mw3Importer.execute(gamePath.getAsPath(), mechDir.getAsPath(), importXML.getAsPath());
	installData.installerWin->displayMsg ("Done");

	// Create registry key
	SET_OP;
	setWin95Mode (gamePath.getAsPath(), "mech3.exe");

	createInstallReport(installData.source, gamePath.getAsPath(), installData.CDtype);
	RETURN true;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// Determine type of CD to install from

int getCDType (const PathChar* source)
{
   // Check for MW3 CDs
   BGCPString MW3Data1Cab(source, CP_SYSTEM);
   MW3Data1Cab.addPathSep("data1.cab");
   if (fileExists (MW3Data1Cab.getAsPath())) {
      INT64 cabSize = getFileSize (MW3Data1Cab.getAsPath());
      switch (cabSize) {
         case 198283576:
         case 197377050: return MW3_CD_eng;
         case 194477962: return MW3_CD_ger;
         case 189090233: return MW3_PM_CD;
      }
   }

   // Check for MW3 installs
   BGCPString MW3MechLibPath(source, CP_SYSTEM);
   MW3MechLibPath.addPathSep("zbd");
   MW3MechLibPath.addPathSep("mechlib.zbd");
   if (fileExists (MW3MechLibPath.getAsPath())) {
      INT64 mechlibSize = getFileSize (MW3MechLibPath.getAsPath());
      switch (mechlibSize) {
         case 2406873: return MW3_eng_installed; // mech3.exe 1.1.1.21
         //case 2406873: return MW3_ger_installed; //
         case 3073867: return MW3_PM_installed; // mech3.exe 1.0.11.5
      }
   }

   BGCPString PRJInMainDir(source, CP_SYSTEM);
   PRJInMainDir.addPathSep("mw2.prj");
   switch (getFileSize (PRJInMainDir.getAsPath())) {
      case 21893380:
      case 28897985: return MW2_31stcc_PE;
      case 43499380: return MW2_31stcc_3DFX;
      case 46409421: return MW2_31stcc_ATI;
      case 58771999: return MW2_31stcc_Matrox;
   }

   if (directoryExists (source, "Windll")) {
      printf ("WinDLL directory found\n");
      BGCPString str(source, CP_SYSTEM);
      str.addPathSep("mercs");
      str.addPathSep("MW2.PRJ");
      INT64 mw2_prj_size = getFileSize(str.getAsPath());
      printf ("Found Mercs" OSPathSepStr "MW2.PRJ with %lli bytes\n", mw2_prj_size); // debug!!!
      if (mw2_prj_size == 41479843) {
         printf ("MW2 Mercs 1.05 with WinDLL detected\n"); // debug!!!
         return MW2_Mercs_DOS; // Version 1.05 with WinDLL directory
      }

      // Battlepack GBL CD?
      str = BGCPString(source, CP_SYSTEM);
      str.addPathSep("GBL");
      str.addPathSep("MW2.PRJ");
      if (getFileSize(str.getAsPath()) == 53579644) 
         return MW2_GBL_BP;
      else {
         // Make sure it is 31stcc BattlePack and not a rare Mercs edition
         // http://www.mech2.org/forum/viewtopic.php?f=8&t=918
         BGCPString str(source, CP_SYSTEM);
         str.addPathSep("MW2");
         str.addPathSep("MW2.PRJ");
         return MW2_31stcc_BP;
      }
   }

   // Check for Titanium
   BGCPString ttPath(source, CP_SYSTEM);
   ttPath.addPathSep("Install");
   ttPath.addPathSep("16bit");
   BGCPString prjPath = ttPath;
   prjPath.addPathSep("mw2.prj");
   ttPath.addPathSep("GBLwin.exe");

   switch (getFileSize(prjPath.getAsPath())) {
      case 64024206:
         if (fileExists (ttPath.getAsPath())) {
            return MW2_GBL_TITAN;
         } else {
            return MW2_31stcc_TITAN;
         }
      case 51841737:
         return MW2_Mercs_TITAN;
   }

   BGCPString NETPRJinRoot(source, CP_SYSTEM);
   NETPRJinRoot += "NET.PRJ";
   if (fileExists (NETPRJinRoot.getAsPath())) {
      return MW2_NetMechInstall;
   }

   if (directoryExists(source, "gbl")) {
      // The size of MW2.PRJ is the same for several editions of GBL
      BGCPString GBLDir(source, CP_SYSTEM);
      GBLDir.addPathSep("gbl");
      BGCPString GBLDOSEXE = GBLDir;
      GBLDOSEXE.addPathSep("gbl.exe");
      BGCPString GBLWinExe = GBLDir;
      GBLWinExe.addPathSep("gblwin.exe");

      bool GBLDosExeExists = fileExists (GBLDOSEXE.getAsPath());
      bool GBLWinExeExists = fileExists (GBLWinExe.getAsPath());

      if (GBLDosExeExists && GBLWinExeExists)
         return MW2_GBL_Hybrid_DOS_Win;
      else if (GBLDosExeExists)
         return MW2_GBL_DOS;
      else if (GBLWinExeExists)
         return MW2_GBL_PE;
   }

   // Mercs CD may contain a mech2 folder also, so test for mercs folder first ...
   if (directoryExists (source, "mercs")) {
       BGCPString MW2PRJ(source, CP_SYSTEM);
       MW2PRJ.addPathSep("mercs");
       MW2PRJ.addPathSep("mw2.prj");
       INT64 MW2PRJsize = getFileSize (MW2PRJ.getAsPath());
       switch (MW2PRJsize) {
           case 51841737: // 3DFX version
               return MW2_Mercs_3DFX;
           case 43200694: // German version
           case 41479843: // Normal english version
               return MW2_Mercs_DOS;
       }
   }

   if (directoryExists(source, "MECH2")) {
      // Search for mw2.prj in mech2 sub directory
      BGCPString MW2PRJ(source, CP_SYSTEM);
      MW2PRJ.addPathSep("MECH2");
      MW2PRJ.addPathSep("MW2.PRJ");
      INT64 MW2PRJsize = getFileSize (MW2PRJ.getAsPath());
      switch (MW2PRJsize) {
         case 22: 
            return MW2_31stcc_Hybrid_DOS_Win;
         case 21893380: 
            if (directoryExists(source, "netmech")) 
               return MW2_NetMech;
            else
               return MW2_31stcc_PE;
         case 19960257: 
         case 22590909:
            return MW2_31stcc_DOS;
      }
   }
   
   return unknownCD;
}

////////////////////////////////////////////////////////////////////////////////
// Install method

bool install (int CDtype, const PathChar* source, const PathChar* imgFN, Installers* installerWin)
{
	InstallData installData;
	installData.source = source;
	installData.CDtype = CDtype;
	installData.imgFN = imgFN;
	installData.installerWin = installerWin;

   switch (CDtype) {
      case MW2_NetMech:
      case MW2_31stcc_DOS:
      case MW2_31stcc_PE:
      case MW2_31stcc_Hybrid_DOS_Win:
         return install_MW2_31stcc_DOS(installData);
      case MW2_GBL_DOS:
      case MW2_GBL_PE:
      case MW2_GBL_Hybrid_DOS_Win:
         return installGBL (installData);
      case MW2_Mercs_DOS:
      case MW2_Mercs_3DFX:
         return installMercs_DOS (installData);
      case MW2_31stcc_TITAN:
         return installMW2_Titan_16bit (installData);
      case MW2_GBL_TITAN:
         return installGBL_Titan (installData);
      case MW2_Mercs_TITAN:
         return installMercs_Titan (installData);
      case MW2_31stcc_BP:
         return install_MW2_BattlePack (installData, "mw2-31stcc-bp");
      case MW2_GBL_BP:
         return install_MW2_BattlePack (installData, "mw2-gbl-bp");
         //case MW2_Mercs_Win:
      case MW2_31stcc_TT_BP:
      case MW2_GBL_TT_BP:
      case MW2_Mercs_TT_BP:
         return installMW2_Titan_8bit (installData);
      case MW2_31stcc_ATI:
      case MW2_31stcc_Matrox:
      case MW2_31stcc_3DFX:
         return installMW2_31stCC_3D_enhanced (installData);
      #ifdef _WIN32
      case MW3_CD_ger:
      case MW3_CD_eng:
      case MW3_PM_CD:
          return installMW3 (installData);
      #endif
      default:
         messageBox("MechVM", "Unknown CD");
         return false;
   }
}

// Get a descriptive string for a CD, given its numeric ID
const char* getCDName (int CDID)
{
	switch (CDID) {
		case MW2_31stcc_DOS:
			return "MW2: 31st Century Combat (DOS)";
		case MW2_31stcc_PE:
			return "MW2: 31st Century Combat (Pentium Edition)";
		case MW2_31stcc_Hybrid_DOS_Win:
			return "MW2: 31st Century Combat (DOS+Win)";
		case MW2_NetMech:
			return "MW2: 31st Century Combat (NetMech)";
		case MW2_NetMechInstall:
			return "MW2: 31st Century Combat (NetMech installed)";
		case MW2_Netmech_InstallPackage:
			return "Netmech install package";
		case MW2_31stcc_BP:
			return "MW2: 31st Century Combat (BattlePack)";
		case MW2_31stcc_TITAN:
			return "MW2: 31st Century Combat (Titanium Trilogy)";
		case MW2_31stcc_3DFX:
			return "MW2: 31st Century Combat (3DFX)";
		case MW2_31stcc_ATI:
			return "MW2: 31st Century Combat (ATI)";
		case MW2_31stcc_TT_BP:
			return "MW2: 31st Century Combat (Battlepack installed from Titanium Trilogy)";
		case MW2_31stcc_Matrox:
			return "MW2: 31st Century Combat (Matrox)";
		case MW2_GBL_DOS:
			return "MW2: Ghost Bear's Legacy (DOS)";
		case MW2_GBL_PE:
			return "MW2: Ghost Bear's Legacy (Pentium Edition)";
		case MW2_GBL_Hybrid_DOS_Win:
			return "MW2: Ghost Bear's Legacy (DOS+Win)";
		case MW2_GBL_BP:
			return "MW2: Ghost Bear's Legacy (Battlepack installed from Titanium Trilogy)";
		case MW2_GBL_TT_BP:
			return "MW2: Ghost Bear's Legacy (Battlepack installed from Titanium Trilogy)";
		case MW2_GBL_TITAN:
			return "MW2: Ghost Bear's Legacy (Titanium Trilogy)";
		case MW2_Mercs_DOS:
			return "MW2: Mercenaries (DOS)";
		case MW2_Mercs_3DFX:
			return "MW2: Mercenaries (3DFX)";
		case MW2_Mercs_TT_BP:
			return "MW2: Mercenaries (Battlepack installed from Titanium Trilogy)";
		case MW2_Mercs_TITAN:
			return "MW2: Mercenaries (Titanium)";
		case MW3_CD_eng:
			return "MW3";
		case MW3_CD_ger:
			return "MW3";
		case MW3_eng_installed:
			return "MW3 (installed)";
		case MW3_ger_installed:
			return "MW3 (installed)";
		case MW3_PM_CD:
			return "MW3: Pirate's Moon";
		case MW3_PM_installed:
			return "MW3: Pirate's Moon (installed)";
		default:
			return "Unknown";
	}
}
