// COBInstall
// Installer stub executable for COB Packager

// Uses a modified version of the 'zlib' general purpose compression library
// which is Copybottom (C) 1995-1998 Jean-loup Gailly and Mark Adler

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <shlobj.h>
#include <direct.h>
#include <errno.h>

#include "Package.h"
#include "resource.h"

enum OverwriteResult
{
	overwriteYes = 1,
	overwriteYesToAll = 2,
	overwriteNo = 3,
	overwriteNoToAll = 4
};

const char *c_sCreatures1RegistryKey = "SOFTWARE\\Millennium Interactive\\Creatures\\1.0";
const char *c_sCreatures2RegistryKey = "SOFTWARE\\CyberLife Technology\\Creatures 2\\1.0";
const char *c_WindowTitle = " - COB Installer";

HINSTANCE g_hAppInstance;
CHAR g_sAppPath[MAX_PATH];
HWND g_hMainWindow;
HWND g_hProgressWindow;

// Creatures 1 variables
BOOL g_bC1Installed = FALSE;
CHAR g_sC1COBsFolder[MAX_PATH];
CHAR g_sC1SpritesFolder[MAX_PATH];
CHAR g_sC1SoundsFolder[MAX_PATH];
CHAR g_sC1TextFilesFolder[MAX_PATH];

// Creatures 2 variables
BOOL g_bC2Installed = FALSE;
CHAR g_sC2COBsFolder[MAX_PATH];
CHAR g_sC2SpritesFolder[MAX_PATH];
CHAR g_sC2SoundsFolder[MAX_PATH];
CHAR g_sC2TextFilesFolder[MAX_PATH];

// Install folders
CHAR g_sCOBsFolder[MAX_PATH];
CHAR g_sSpritesFolder[MAX_PATH];
CHAR g_sSoundsFolder[MAX_PATH];
CHAR g_sTextFilesFolder[MAX_PATH];

LPMALLOC g_pMalloc;
LPSHELLFOLDER g_pShellFolder;
LPITEMIDLIST g_pidlBrowseStartFolder;

Package g_package;

void Shutdown()
{
	g_pShellFolder->Release();
}

void GetLongFilename(LPSTR sPath)
{
	WCHAR sTempPath[MAX_PATH];
	ULONG nPathProcessed;
	LPITEMIDLIST pidlTempPath;

	MultiByteToWideChar(CP_ACP, 0, sPath, -1, sTempPath, strlen(sPath) + 1);
	g_pShellFolder->ParseDisplayName(NULL, NULL, sTempPath, &nPathProcessed, &pidlTempPath, NULL);
	SHGetPathFromIDList(pidlTempPath, sPath);
}

void GetDefaultFolders()
{
	LONG nRes;
	HKEY hKey;
	DWORD nSize;

	GetCurrentDirectory(MAX_PATH, g_sC1COBsFolder);
	GetCurrentDirectory(MAX_PATH, g_sC1SpritesFolder);
	GetCurrentDirectory(MAX_PATH, g_sC1SoundsFolder);
	GetCurrentDirectory(MAX_PATH, g_sC1TextFilesFolder);

	// Get Creatures 1 settings
	nRes = RegOpenKey(HKEY_LOCAL_MACHINE, c_sCreatures1RegistryKey, &hKey);
	if (nRes == ERROR_SUCCESS)
	{
		g_bC1Installed = TRUE;

		nSize = MAX_PATH;
		nRes = RegQueryValueEx(hKey, "Main Directory", NULL, NULL, (LPBYTE)g_sC1COBsFolder, &nSize);
		if (nRes != ERROR_SUCCESS)
		{
			nRes = RegCloseKey(hKey);
			MessageBox(NULL, "Could not get the Creatures 1 Main Directory from the registry!", "COB Installer", MB_OK | MB_ICONWARNING);
			return;
		}

		GetLongFilename(g_sC1COBsFolder);

		nSize = MAX_PATH;
		nRes = RegQueryValueEx(hKey, "Image Directory", NULL, NULL, (LPBYTE)g_sC1SpritesFolder, &nSize);
		if (nRes != ERROR_SUCCESS)
		{
			nRes = RegCloseKey(hKey);
			MessageBox(NULL, "Could not get the Creatures 1 Image Directory from the registry!", "COB Installer", MB_OK | MB_ICONWARNING);
			return;
		}

		GetLongFilename(g_sC1SpritesFolder);

		nSize = MAX_PATH;
		nRes = RegQueryValueEx(hKey, "Sound Directory", NULL, NULL, (LPBYTE)g_sC1SoundsFolder, &nSize);
		if (nRes != ERROR_SUCCESS)
		{
			nRes = RegCloseKey(hKey);
			MessageBox(NULL, "Could not get the Creatures 1 Sound Directory from the registry!", "COB Installer", MB_OK | MB_ICONWARNING);
			return;
		}

		GetLongFilename(g_sC1SoundsFolder);

		strcpy(g_sC1TextFilesFolder, g_sC1COBsFolder);

		nRes = RegCloseKey(hKey);
	}

	GetCurrentDirectory(MAX_PATH, g_sC2COBsFolder);
	GetCurrentDirectory(MAX_PATH, g_sC2SpritesFolder);
	GetCurrentDirectory(MAX_PATH, g_sC2SoundsFolder);
	GetCurrentDirectory(MAX_PATH, g_sC2TextFilesFolder);

		// Get Creatures 2 settings
	nRes = RegOpenKey(HKEY_LOCAL_MACHINE, c_sCreatures2RegistryKey, &hKey);
	if (nRes == ERROR_SUCCESS)
	{
		g_bC2Installed = TRUE;

		nSize = MAX_PATH;
		nRes = RegQueryValueEx(hKey, "Objects Directory", NULL, NULL, (LPBYTE)g_sC2COBsFolder, &nSize);
		if (nRes != ERROR_SUCCESS)
		{
			nRes = RegCloseKey(hKey);
			MessageBox(NULL, "Could not get the Creatures 2 Object Directory from the registry!", "COB Installer", MB_OK | MB_ICONWARNING);
			return;
		}

		nSize = MAX_PATH;
		nRes = RegQueryValueEx(hKey, "Images Directory", NULL, NULL, (LPBYTE)g_sC2SpritesFolder, &nSize);
		if (nRes != ERROR_SUCCESS)
		{
			nRes = RegCloseKey(hKey);
			MessageBox(NULL, "Could not get the Creatures 2 Image Directory from the registry!", "COB Installer", MB_OK | MB_ICONWARNING);
			return;
		}

		nSize = MAX_PATH;
		nRes = RegQueryValueEx(hKey, "Sounds Directory", NULL, NULL, (LPBYTE)g_sC2SoundsFolder, &nSize);
		if (nRes != ERROR_SUCCESS)
		{
			nRes = RegCloseKey(hKey);
			MessageBox(NULL, "Could not get the Creatures 2 Sound Directory from the registry!", "COB Installer", MB_OK | MB_ICONWARNING);
			return;
		}

		strcpy(g_sC2TextFilesFolder, g_sC2COBsFolder);

		nRes = RegCloseKey(hKey);
	}
}

BOOL CALLBACK OverwriteDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	char *sFilename;
	char sStaticWarning[256];
	char sOverwriteWarning[256 + MAX_PATH];

	switch (uMsg)
	{
		case WM_INITDIALOG:
			sFilename = (char *)lParam;
			Static_GetText(GetDlgItem(hwndDlg, IDC_STATIC_WARNING), sStaticWarning, 255);
			sprintf(sOverwriteWarning, sStaticWarning, sFilename);
			Static_SetText(GetDlgItem(hwndDlg, IDC_STATIC_WARNING), sOverwriteWarning);
			return TRUE;

		case WM_COMMAND:
			switch (LOWORD(wParam))
			{
				case IDYES:
					if (HIWORD(wParam) == BN_CLICKED)
						EndDialog(hwndDlg, overwriteYes);
					break;

				case IDYESTOALL:
					if (HIWORD(wParam) == BN_CLICKED)
						EndDialog(hwndDlg, overwriteYesToAll);
					break;

				case IDNO:
					if (HIWORD(wParam) == BN_CLICKED)
						EndDialog(hwndDlg, overwriteNo);
					break;

				case IDNOTOALL:
					if (HIWORD(wParam) == BN_CLICKED)
						EndDialog(hwndDlg, overwriteNoToAll);
					break;

				default:
					return FALSE;
			}
			break;

		default:
			return FALSE;
	}

	return TRUE;
}

BOOL CheckFolderExists(char *sFolder)
{
    struct _finddata_t finddata;
	long hFind;
	char sMessage[255];
	int nResult;

	hFind = _findfirst(sFolder, &finddata);
	if (hFind != -1)
	{
		if (!(finddata.attrib & _A_SUBDIR))
		{
			sprintf(sMessage, "%s is not a folder.", sFolder);
			MessageBox(g_hMainWindow, sMessage, "COB Installer", MB_OK | MB_ICONERROR);
			return FALSE;
		}

		return TRUE;
	}

	sprintf(sMessage, "%s does not exist, shall I create it?", sFolder);
	nResult = MessageBox(g_hMainWindow, sMessage, "COB Installer", MB_OKCANCEL | MB_ICONQUESTION);

	if (nResult == IDOK)
	{
		if (_mkdir(sFolder) == 0)
			return TRUE;
		else
		{
			switch (errno)
			{
				case EACCES:
					sprintf(sMessage, "%s already exists.", sFolder);
					break;

				case ENOENT:
					sprintf(sMessage, "Path to %s was not found.", sFolder);
					break;
			}

			MessageBox(g_hMainWindow, sMessage, "COB Installer", MB_OK | MB_ICONERROR);
			return FALSE;
		}
	}

	return FALSE;
}

BOOL CanOverwriteFile(char *sFilename)
{
    struct _finddata_t finddata;
	long hFind;
	static int nResult;

	if (nResult == overwriteYesToAll)
		return TRUE;

	if (nResult != overwriteNoToAll)
		nResult = overwriteYes;

	hFind = _findfirst(sFilename, &finddata);
	if (hFind != -1)
	{
		if (nResult == overwriteNoToAll)
			return FALSE;
		nResult = DialogBoxParam(g_hAppInstance, MAKEINTRESOURCE(IDD_OVERWRITE), g_hProgressWindow, DLGPROC(OverwriteDialogProc), (LPARAM)finddata.name);
		_findclose(hFind);
	}

	switch (nResult)
	{
		case overwriteYes:
		case overwriteYesToAll:
			return TRUE;

		case overwriteNo:
		case overwriteNoToAll:
			return FALSE;

		case -1:
		default:
			MessageBox(NULL, "Fatal error.", "COB Installer", MB_OK | MB_ICONERROR);
			Shutdown();
			exit(-1);
			break;
	}
}

BOOL ExtractFile(PackagedFile *file)
{
	char *sExtension;
	FILE *fFile;
	char sDestFilename[MAX_PATH];
	
	sExtension = file->Filename + strlen(file->Filename) - 3;

	if ((stricmp(sExtension, "cob") == 0) || (stricmp(sExtension, "rcb") == 0))
		strcpy(sDestFilename, g_sCOBsFolder);
	else if ((stricmp(sExtension, "spr") == 0) || (stricmp(sExtension, "s16") == 0))
		strcpy(sDestFilename, g_sSpritesFolder);
	else if (stricmp(sExtension, "wav") == 0)
		strcpy(sDestFilename, g_sSoundsFolder);
	else if (stricmp(sExtension, "txt") == 0)
		strcpy(sDestFilename, g_sTextFilesFolder);

	strcat(sDestFilename, file->Filename);

	if (CanOverwriteFile(sDestFilename))
	{
		fFile = fopen(sDestFilename, "wb");
		if (fFile == NULL)
			return FALSE;

		if (file->DataLength > 0)
			fwrite(file->Data, file->DataLength, 1, fFile);

		fclose(fFile);
	}

	return TRUE;
}

/*BOOL ResetC2DisplayType()
{
	HKEY hKey;
	LONG nRes;
	DWORD nTemp;

	// Get Creatures 2 settings
	nRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_sCreatures2RegistryKey, 0, KEY_SET_VALUE, &hKey);
	if (nRes != ERROR_SUCCESS)
		return FALSE;

	nTemp = 0;
	nRes = RegSetValueEx(hKey, "Display Type", 0, REG_DWORD, (CONST BYTE *)&nTemp, sizeof(nTemp));
	if (nRes != ERROR_SUCCESS)
		return FALSE;

	RegCloseKey(hKey);

	return TRUE;
}*/

BOOL CALLBACK ProgressDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HWND hProgressBar;

	switch (uMsg)
	{
		case WM_INITDIALOG:
			Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), "Copying files...");

			hProgressBar = GetDlgItem(hwndDlg, IDC_PROGRESS);

			SendMessage(hProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, (g_package.FileCount - 1)));
			SendMessage(hProgressBar, PBM_SETSTEP, 1, 0);
			SendMessage(hProgressBar, PBM_SETPOS, 0, 0);
			return TRUE;

		default:
			return FALSE;
	}

	return TRUE;
}

void DoInstall()
{
	int i;
	MSG msg;
	HWND hProgressBar;

	g_hProgressWindow = CreateDialog(g_hAppInstance, MAKEINTRESOURCE(IDD_PROGRESS), g_hMainWindow, (DLGPROC)ProgressDialogProc);
	ShowWindow(g_hProgressWindow, SW_SHOWDEFAULT);

	hProgressBar = GetDlgItem(g_hProgressWindow, IDC_PROGRESS);

	for (i = 0; i < g_package.FileCount; i++)
	{
		SendMessage(hProgressBar, PBM_SETPOS, i, 0);

		if (!ExtractFile(&g_package.Files[i]))
		{
			MessageBox(g_hProgressWindow, "Could not extract file!", "COB Installer", MB_OK | MB_ICONERROR);
			break;
		}
	}

	DestroyWindow(g_hProgressWindow);

	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	/*if ((g_package.Flags & cPFCreatures2) && g_bC2Installed)
		if (!ResetC2DisplayType())
			MessageBox(g_hMainWindow, "Could not force Creatures 2 to reconvert its sprites.", "COB Install", MB_OK | MB_ICONWARNING);*/

	MessageBox(g_hMainWindow, "Installation complete.", "COB Installer", MB_OK | MB_ICONINFORMATION);

	g_hProgressWindow = NULL;
}

BOOL CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case WM_INITDIALOG:
			break;

		case WM_SYSCOMMAND:
			switch (wParam)
			{
				case SC_CLOSE:
					EndDialog(hwndDlg, 0);
					break;

				default:
					return FALSE;
			}
			break;
			
		case WM_COMMAND:
			switch (wParam)
			{
				case IDOK:
					EndDialog(hwndDlg, 0);
					break;

				default:
					return FALSE;
			};
			break;

		default:
			return FALSE;
	}

	return TRUE;
}

int CALLBACK BrowseForFolderCallback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
	if (uMsg == BFFM_INITIALIZED)
	{
		SendMessage(hwnd, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)g_pidlBrowseStartFolder);
	}

	return 0;
}
 
BOOL BrowseForFolder(HWND hwndOwner, LPSTR sPath)
{
	BROWSEINFO bi;
	LPSTR lpBuffer;
    LPITEMIDLIST pidlBrowse;    // PIDL selected by user
	BOOL bCancel;
	LPOLESTR sStartPath;
	ULONG nPathProcessed;
	
    // Allocate a buffer to receive browse information.
    if ((lpBuffer = (LPSTR)g_pMalloc->Alloc(MAX_PATH)) == NULL)
		return FALSE;

	sStartPath = new WCHAR[strlen(sPath) + 1];
	MultiByteToWideChar(CP_ACP, 0, sPath, -1, sStartPath, strlen(sPath) + 1);

	g_pShellFolder->ParseDisplayName(hwndOwner, NULL, sStartPath, &nPathProcessed, &g_pidlBrowseStartFolder, NULL);

    // Fill in the BROWSEINFO structure.
	bi.hwndOwner = hwndOwner;
    bi.pidlRoot = NULL;
	bi.pszDisplayName = lpBuffer;
    bi.lpszTitle = "Select folder to extract files into:";
	bi.ulFlags = BIF_RETURNONLYFSDIRS;
    bi.lpfn = &BrowseForFolderCallback;
	bi.lParam = 0; 

    bCancel = TRUE;

	// Browse for a folder and return its PIDL.
    pidlBrowse = SHBrowseForFolder(&bi);
	if (pidlBrowse != NULL)
	{
		// Get the path from the PIDL return by SHBrowseForFolder
		if (SHGetPathFromIDList(pidlBrowse, lpBuffer))
			strcpy(sPath, lpBuffer);

        // Free the PIDL returned by SHBrowseForFolder.
        g_pMalloc->Free(pidlBrowse);

		bCancel = FALSE;
	}
	
	// Clean up.
    g_pMalloc->Free(lpBuffer);

	return !bCancel;
}

BOOL CALLBACK MainDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case WM_INITDIALOG:
			if (g_package.Flags & cPFCreatures2)
			{
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_COBS), g_sC2COBsFolder);
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_SPRITES), g_sC2SpritesFolder);
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_SOUNDS), g_sC2SoundsFolder);
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_TEXTFILES), g_sC2TextFilesFolder);
			}
			else
			{
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_COBS), g_sC1COBsFolder);
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_SPRITES), g_sC1SpritesFolder);
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_SOUNDS), g_sC1SoundsFolder);
				Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_TEXTFILES), g_sC1TextFilesFolder);
			}
			break;

		case WM_DESTROY:
			PostMessage(hwndDlg, WM_QUIT, 0, 0);
			break;

		case WM_SYSCOMMAND:
			switch (wParam)
			{
				case SC_CLOSE:
					DestroyWindow(hwndDlg);
					break;

				default:
					return FALSE;
			}
			break;
			
		case WM_COMMAND:
			switch (wParam)
			{
				case IDOK:
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_COBS), g_sCOBsFolder, MAX_PATH);
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_SPRITES), g_sSpritesFolder, MAX_PATH);
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_SOUNDS), g_sSoundsFolder, MAX_PATH);
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_TEXTFILES), g_sTextFilesFolder, MAX_PATH);

					if (g_sCOBsFolder[strlen(g_sCOBsFolder) - 1] == '\\')
						g_sCOBsFolder[strlen(g_sCOBsFolder) - 1] = 0;
					if (g_sSpritesFolder[strlen(g_sSpritesFolder) - 1] == '\\')
						g_sSpritesFolder[strlen(g_sSpritesFolder) - 1] = 0;
					if (g_sSoundsFolder[strlen(g_sSoundsFolder) - 1] == '\\')
						 g_sSoundsFolder[strlen(g_sSoundsFolder) - 1] = 0;
					if (g_sTextFilesFolder[strlen(g_sTextFilesFolder) - 1] == '\\')
						g_sTextFilesFolder[strlen(g_sTextFilesFolder) - 1] = 0;

					if (!CheckFolderExists(g_sCOBsFolder) || 
						!CheckFolderExists(g_sSpritesFolder) || 
						!CheckFolderExists(g_sSoundsFolder) || 
						!CheckFolderExists(g_sTextFilesFolder))
						break;

					strcat(g_sCOBsFolder, "\\");
					strcat(g_sSpritesFolder, "\\");
					strcat(g_sSoundsFolder, "\\");
					strcat(g_sTextFilesFolder, "\\");

					EnableWindow(hwndDlg, FALSE);
					DoInstall();
					EnableWindow(hwndDlg, TRUE);
					DestroyWindow(hwndDlg);
					break;

				case IDCANCEL:
					DestroyWindow(hwndDlg);
					break;

				case IDC_ABOUT:
					DialogBox(g_hAppInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndDlg, (DLGPROC)AboutDialogProc);
					break;

				case IDC_BROWSECOBS:
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_COBS), g_sCOBsFolder, MAX_PATH);
					if (BrowseForFolder(hwndDlg, g_sCOBsFolder))
						Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_COBS), g_sCOBsFolder);
					break;

				case IDC_BROWSESPRITES:
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_SPRITES), g_sSpritesFolder, MAX_PATH);
					if (BrowseForFolder(hwndDlg, g_sSpritesFolder))
						Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_SPRITES), g_sSpritesFolder);
					break;

				case IDC_BROWSESOUNDS:
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_SOUNDS), g_sSoundsFolder, MAX_PATH);
					if (BrowseForFolder(hwndDlg, g_sSoundsFolder))
						Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_SOUNDS), g_sSoundsFolder);
					break;

				case IDC_BROWSETEXTFILES:
					Edit_GetText(GetDlgItem(hwndDlg, IDC_EDIT_TEXTFILES), g_sTextFilesFolder, MAX_PATH);
					if (BrowseForFolder(hwndDlg, g_sTextFilesFolder))
						Edit_SetText(GetDlgItem(hwndDlg, IDC_EDIT_TEXTFILES), g_sTextFilesFolder);
					break;

				default:
					return FALSE;
			}
			break;

		default:
			return FALSE;
	}

	return TRUE;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;
	LPTSTR sWindowTitle;

	GetModuleFileName(NULL, g_sAppPath, MAX_PATH);

	SHGetMalloc(&g_pMalloc);

	SHGetDesktopFolder(&g_pShellFolder);

	g_hAppInstance = hInstance;

	InitCommonControls();

	GetDefaultFolders();

	ZeroMemory(&g_package, sizeof(g_package));
	if (LoadPackageFromExe(g_sAppPath, &g_package))
	{
		sWindowTitle = new CHAR[strlen(g_package.Title) + strlen(c_WindowTitle) + 1];
		strcpy(sWindowTitle, g_package.Title);
		strcat(sWindowTitle, c_WindowTitle);
	}

	g_hMainWindow = CreateDialog(g_hAppInstance, MAKEINTRESOURCE(IDD_MAIN), HWND_DESKTOP, (DLGPROC)MainDialogProc);
	ShowWindow(g_hMainWindow, SW_SHOWDEFAULT);
	SetWindowText(g_hMainWindow, sWindowTitle);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (IsDialogMessage(g_hMainWindow, &msg))
			continue;

		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

    return msg.wParam;
}
