
#include "stdafx.h"
#include <stdio.h>
#include <shlobj.h>
#include <io.h>
#include "util.h"
#include "GlobalData.h"
#include "file.h"


int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
  LPCTSTR p_szInitialDir = (LPCTSTR)lpData;

  if( uMsg == BFFM_INITIALIZED && p_szInitialDir && strlen( p_szInitialDir ) > 0 )
  {
    ::SendMessage( hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)p_szInitialDir );
  }

  return 0;
}

// Ask the user to choose a directory and return the selected directory.
// Return an empty string if no dir selected.
// Parameter is the string that willl be displayed in dlg box (ie: "Choose directory" )
CString fn_csBrowseDirectory( LPCTSTR _p_szTitle, LPCTSTR _p_szDefaultDir /*= NULL*/ ) 
{
	BROWSEINFO stOpen;
	ITEMIDLIST	*p_stItem;
	TCHAR szDirName[_MAX_PATH];

	memset( &stOpen, 0, sizeof(stOpen) );
	stOpen.hwndOwner = g_stTheGlobalData.p_oMainDlg->m_hWnd; 
	stOpen.pidlRoot = NULL;
	stOpen.pszDisplayName = szDirName; 
	stOpen.lpszTitle = _p_szTitle; 
	stOpen.ulFlags = BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS|BIF_DONTGOBELOWDOMAIN;
	stOpen.lpfn = BrowseCallbackProc;
	stOpen.lParam = (LPARAM)_p_szDefaultDir;
	stOpen.iImage = 0;
	
	if( (p_stItem = SHBrowseForFolder( &stOpen )) != NULL )
	{
		SHGetPathFromIDList( p_stItem, szDirName );
		return CString( szDirName );
	}

	return CString( _T("") );
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// Read version.ini file and update GlobalData
BOOL fn_bReadVersionFile()
{
	TCHAR szText[1024];
	char szFileName[512];
	FILE *hVersion;

	// Empty array of directory
	g_stTheGlobalData.a_csVersionDirectories.RemoveAll();
	g_stTheGlobalData.a_csVersionDirectories.SetSize( 0, 5 );

	sprintf( szFileName, "%s\\version.ini", (const char *)g_stTheGlobalData.csMainDirectory );
	hVersion = fopen( szFileName, "rb" );

	if( ! hVersion ) 
	{
/*
		sprintf( szText, "Error: Cannot read file '%s'", szFileName );
		AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
		return FALSE;
*/
		// Assuming all game data is in GameData.
		return TRUE;
	}

	// Check  directories in version.ini 
	char szOtherDataDirs[255];
	char *szToken;
	char szDelimiters[] = " ;,\t";

	fgets( szOtherDataDirs, 255, hVersion );
	fclose( hVersion );

	// suppress last char (EOL)
	szOtherDataDirs[strlen(szOtherDataDirs)-2] = NULL;

	// get all directories
	szToken = strtok( szOtherDataDirs, szDelimiters );
	for( ; szToken; szToken = strtok( NULL, szDelimiters ) )
	{
		g_stTheGlobalData.a_csVersionDirectories.Add( szToken );

		// Test access to directories in version.ini
		sprintf( szFileName, "%s\\%s", (const char *)g_stTheGlobalData.csMainDirectory, szToken );
		DWORD dwAttr = GetFileAttributes( szFileName );
		if( (dwAttr | FILE_ATTRIBUTE_DIRECTORY) == 0 || dwAttr == 0xffffffff )
		{
			g_stTheGlobalData.a_csVersionDirectories.RemoveAll();
			sprintf( szText, "Error: Version.ini contains reference to directory '%s', that is not acessible.", szFileName );
			AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
			return FALSE;
		}
	}

	return TRUE;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// Read SoundVersion.ini file and update GlobalData
BOOL fn_bReadSoundVersionFile()
{
	TCHAR szText[1024];
	char szFileName[512];
	FILE *hVersion;

	// Empty array of directory
	g_stTheGlobalData.a_csSoundVersionDirectories.RemoveAll();
	g_stTheGlobalData.a_csSoundVersionDirectories.SetSize( 0, 5 );

	sprintf( szFileName, "%s\\SoundVersion.ini", (const char *)g_stTheGlobalData.csMainDirectory );
	hVersion = fopen( szFileName, "rb" );

	if( ! hVersion ) 
	{
		// No SoundVersion.ini, check version.ini
		if(	g_stTheGlobalData.a_csVersionDirectories.GetSize() > 0 )
		{
			g_stTheGlobalData.a_csSoundVersionDirectories.Copy( g_stTheGlobalData.a_csVersionDirectories );
		}
		// else, we assume all sound data is in GameData.

		return TRUE;
	}

	// Check  directories in version.ini 
	char szOtherDataDirs[255];
	char *szToken;
	char szDelimiters[] = " ;,\t";

	fgets( szOtherDataDirs, 255, hVersion );
	fclose( hVersion );

	// suppress last char (EOL)
	szOtherDataDirs[strlen(szOtherDataDirs)-2] = NULL;

	// get all directories
	szToken = strtok( szOtherDataDirs, szDelimiters );
	for( ; szToken; szToken = strtok( NULL, szDelimiters ) )
	{
		g_stTheGlobalData.a_csSoundVersionDirectories.Add( szToken );

		// Test access to directories in version.ini
		sprintf( szFileName, "%s\\%s", (const char *)g_stTheGlobalData.csMainDirectory, szToken );
		DWORD dwAttr = GetFileAttributes( szFileName );
		if( (dwAttr | FILE_ATTRIBUTE_DIRECTORY) == 0 || dwAttr == 0xffffffff )
		{
			g_stTheGlobalData.a_csSoundVersionDirectories.RemoveAll();
			sprintf( szText, "Error: SoundVersion.ini contains reference to directory '%s' that is not acessible.", szFileName );
			AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
			return FALSE;
		}
	}

	return TRUE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// Add a line of text in the CEdit control used for displaying results.
// Line must be terminated by "\r\n" for carriage return.
void fn_vOutputLine( const TCHAR *_p_szLine )
{
	int iTxtLen = g_stTheGlobalData.p_oEditResults -> GetWindowTextLength();
	g_stTheGlobalData.p_oEditResults -> SetSel( iTxtLen, iTxtLen, TRUE );

	g_stTheGlobalData.p_oEditResults -> ReplaceSel( _p_szLine );
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

UINT fn_uiThreadCopySoundDirectory( LPVOID _p_stParam )
{
	char szSourceDir[512], szTargetDir[512];
	tdeStatus eStatus;

	_p_stParam = _p_stParam;

	fn_vOutputLine( "Copying sound directory...\r\n" );

	sprintf( szTargetDir, "%s\\World\\Sound", (const char *)g_stTheGlobalData.csBinDirectory );


	// Copy from all directories of soundversion.ini
	for( int i=g_stTheGlobalData.a_csSoundVersionDirectories.GetSize()-1; i>=0; i-- )
	{
		sprintf( szSourceDir, "\tCopying sound from %s\r\n", (LPCTSTR)g_stTheGlobalData.a_csSoundVersionDirectories[i] );
		fn_vOutputLine( szSourceDir );
		sprintf( szSourceDir, "%s\\%s\\World\\Sound\\*.*", (const char *)g_stTheGlobalData.csMainDirectory, (LPCTSTR)g_stTheGlobalData.a_csSoundVersionDirectories[i] );
		if( (eStatus =  fn_eCopyFileOrDirectory( szSourceDir, szTargetDir )) != STATUS_C_OK )
			AfxEndThread( (UINT)eStatus );
	}

	// Copy sound from Gamedata
	fn_vOutputLine( "\tCopying sound from Gamedata\r\n" );
	sprintf( szSourceDir, "%s\\Gamedata\\World\\Sound\\*.*", (const char *)g_stTheGlobalData.csMainDirectory );
	if( (eStatus =  fn_eCopyFileOrDirectory( szSourceDir, szTargetDir )) != STATUS_C_OK )
		AfxEndThread( eStatus );

	fn_vOutputLine( "\tCopy complete !\r\n\r\n" );

	AfxEndThread( STATUS_C_OK );
	return STATUS_C_OK;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Update the file startprg.ini (old version)
void fn_vUpdateStartPrg_old()
{
	char szStartPrg[512];
	char szSection[20];
	char szVersion[20];
	char szKey[50];

/*
[Start]
DscCreated=1
AskAtEachTime=1
UseBinaryData=1
GDReleaseBin=databin
BinReleaseIsOk=1
*/
	sprintf( szStartPrg, "%s\\StartPrg.ini", (const char *)g_stTheGlobalData.csMainDirectory );
	strcpy( szSection, "START") ;
	if( g_stTheGlobalData.bDebugVersion )
		strcpy( szVersion, "Debug" );
	else
		strcpy( szVersion, "Release" );

	WritePrivateProfileString( szSection, "DscCreated", "1", szStartPrg );
	WritePrivateProfileString( szSection, "UseBinaryData", "1", szStartPrg );

	sprintf( szKey, "Bin%sIsOk", szVersion );
	WritePrivateProfileString( szSection, szKey, "1", szStartPrg );

	// If bin directory is a subdir of main
	int iMainLength = g_stTheGlobalData.csMainDirectory.GetLength();
	int iBinLength  = g_stTheGlobalData.csBinDirectory.GetLength();
	CString csDir;

	if( strnicmp( g_stTheGlobalData.csBinDirectory, g_stTheGlobalData.csMainDirectory, iMainLength ) == 0 )
	{
		if( iMainLength == iBinLength )
			csDir = ".";
		else
			csDir = g_stTheGlobalData.csBinDirectory.Mid( iMainLength+1 );
	}
	else
	{
		csDir = g_stTheGlobalData.csBinDirectory;
	}

	sprintf( szKey, "GD%sBin", szVersion );
	WritePrivateProfileString( szSection, szKey, csDir, szStartPrg );
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Update the file startprg.ini
void fn_vUpdateStartPrg()
{
	char szStartPrg[512];
	char szSection[20];

	sprintf( szStartPrg, "%s\\StartPrg.ini", (const char *)g_stTheGlobalData.csMainDirectory );

	if( g_stTheGlobalData.bDebugVersion )
		strcpy( szSection, "DEBUG" );
	else
		strcpy( szSection, "RELEASE" );

	WritePrivateProfileString( szSection, "DscCreated", "1", szStartPrg );
	WritePrivateProfileString( szSection, "BinDataIsOk", "1", szStartPrg );

	// If bin directory is a subdir of main
	int iMainLength = g_stTheGlobalData.csMainDirectory.GetLength();
	int iBinLength  = g_stTheGlobalData.csBinDirectory.GetLength();
	CString csDir;

	if( strnicmp( g_stTheGlobalData.csBinDirectory, g_stTheGlobalData.csMainDirectory, iMainLength ) == 0 )
	{
		if( iMainLength == iBinLength )
			csDir = ".";
		else
			csDir = g_stTheGlobalData.csBinDirectory.Mid( iMainLength+1 );
	}
	else
	{
		csDir = g_stTheGlobalData.csBinDirectory;
	}

	WritePrivateProfileString( szSection, "BinDirectory", csDir, szStartPrg );
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Try to find szSubString  in szString.
// Comparaison is case INSENSITIVE.
const char *stristr( const char *szString, const char *szSubString )
{
	const char *p_szSrcCur;
	const char *p_szToFindCur;
	const char *p_szSrcStartFind;


	p_szSrcStartFind = szString; 
	p_szToFindCur    = szSubString;
	for( p_szSrcCur = szString; *p_szSrcCur && *p_szToFindCur; p_szSrcCur++ )
	{
		if( tolower( *p_szSrcCur ) == tolower( *p_szToFindCur ) )
		{
			if( ! p_szSrcStartFind )
				p_szSrcStartFind = p_szSrcCur;
			p_szToFindCur ++;
		}
		else
		{
			p_szToFindCur    = szSubString;
			p_szSrcStartFind = NULL;
		}
	}

	if( *p_szToFindCur == '\0' )
		return p_szSrcStartFind;

	return NULL;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


void fn_vDisableRelocTableBigFile()
{
	char szFile[512];
	sprintf( szFile, "%s\\StartPrg.ini", (LPCTSTR)g_stTheGlobalData.csMainDirectory );
	WritePrivateProfileString( "Start", "UsesBigFile", "No", szFile );
}



// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

BOOL fn_bCheckFileExistanceInStartupDir( CString csFileName )
{
	char szFileName[512];

	sprintf( szFileName, "%s\\%s", (LPCTSTR)g_stTheGlobalData.csStartupDirectory, (LPCTSTR)csFileName );

	if( _access( szFileName, 4 ) == 0 )
		return TRUE;

	fn_vOutputLine( "\tFile '" );
	fn_vOutputLine( szFileName );
	fn_vOutputLine( "' is missing.\r\n" );
	return FALSE;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


// When user changes main directory, we validate his choice
// and we read some usefull files (ie: version.ini, soundversion.ini)
// Note: game.dsc is red later, when user goes into the panel "Levels"
BOOL fn_bValidateMainDirectorySetting()
{
	TCHAR szFilename[512];
	TCHAR szText[1024];

	// Clear associated settings
	g_stTheGlobalData.a_csLevelNames.RemoveAll();
	g_stTheGlobalData.a_csSoundVersionDirectories.RemoveAll();
	g_stTheGlobalData.a_csVersionDirectories.RemoveAll();

	// Get new directory
	g_stTheGlobalData.csMainDirectory.TrimLeft();
	g_stTheGlobalData.csMainDirectory.TrimRight();

	if( g_stTheGlobalData.csMainDirectory.IsEmpty() )
	{
		AfxMessageBox( "Please choose a path for main directory.", MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	// Remove trailing '\' if it exists
	int iLength = g_stTheGlobalData.csMainDirectory.GetLength();
	if( iLength > 0 && g_stTheGlobalData.csMainDirectory.GetAt( iLength-1 ) == '\\' )
	{
		g_stTheGlobalData.csMainDirectory.SetAt( iLength-1, ' ' );
		g_stTheGlobalData.csMainDirectory.TrimRight();
	}


	// Test if we can write in this directory
	if( _access( g_stTheGlobalData.csMainDirectory, 06 ) == -1 )
	{
		sprintf( szText, "Error: Main directory is not valid !\n(The directory '%s' is not accessible.)", (const char *)g_stTheGlobalData.csMainDirectory );
		AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	// Test if the directory gamedata is present
	sprintf( szFilename, "%s\\Gamedata", (const char *)g_stTheGlobalData.csMainDirectory );
	if( _access( szFilename, 04) == -1 )
	{
		sprintf( szText, "Error: Main directory is not valid !\nThe directory '%s' is not accessible.", szFilename );
		AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	// Read version.ini
	if( ! fn_bReadVersionFile() ) return FALSE;

	// Read SoundVersion.ini. Must be read after version.ini because it can use directory found in it 
	if( ! fn_bReadSoundVersionFile() ) return FALSE;

	// We read game.dsc for list of maps
	if( ! g_stTheGlobalData.p_oMainDlg -> m_fn_bRefreshLevelList() )
		return FALSE;

	// We have read everything, all seems to be ok.
	return TRUE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Test validity of selected binary directory.
BOOL fn_bValidateBinDirectorySetting()
{
	g_stTheGlobalData.csBinDirectory.TrimLeft();
	g_stTheGlobalData.csBinDirectory.TrimRight();

	if( g_stTheGlobalData.csBinDirectory.IsEmpty() )
	{
		AfxMessageBox( "Please choose a path for binary directory.", MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	// Remove trailing '\' if it exists
	int iLength = g_stTheGlobalData.csBinDirectory.GetLength();
	if( iLength > 0 && g_stTheGlobalData.csBinDirectory.GetAt( iLength-1 ) == '\\' )
	{
		g_stTheGlobalData.csBinDirectory.SetAt( iLength-1, ' ' );
		g_stTheGlobalData.csBinDirectory.TrimRight();
	}

	// Try to create the directory
	if( fn_eCreateDirectory( g_stTheGlobalData.csBinDirectory ) == STATUS_C_Error )
	{
		AfxMessageBox( "Error: Unable to create binary directory !", MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	// We need write access on this directory
	if( _access( g_stTheGlobalData.csBinDirectory, 06 ) == -1 )
	{
		TCHAR szText[1024];
		sprintf( szText, "Error: Binary directory is not valid !\n(The directory '%s' is not accessible.)", (const char *)g_stTheGlobalData.csBinDirectory );
		AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	return TRUE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Test validity of selected game executable
BOOL fn_bValidateGameExeSetting()
{
	TCHAR szFilename[512];

	// Get new file name
	g_stTheGlobalData.csExeName.TrimLeft();
	g_stTheGlobalData.csExeName.TrimRight();

	if( g_stTheGlobalData.csExeName.IsEmpty() )
	{
		AfxMessageBox( "Please choose the engine executable.", MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	if( g_stTheGlobalData.bDebugVersion )
		g_stTheGlobalData.csExecutableNames[1] = g_stTheGlobalData.csExeName;
	else
		g_stTheGlobalData.csExecutableNames[0] = g_stTheGlobalData.csExeName;

	sprintf( szFilename, "%s\\%s", g_stTheGlobalData.csMainDirectory, g_stTheGlobalData.csExeName );

	// We need read access on this file
	if( _access( szFilename, 06 ) == -1 )
	{
		TCHAR szText[1024];
		sprintf( szText, "Error: File '%s' is not valid ! (not accessible)", szFilename );
		AfxMessageBox( szText, MB_OK | MB_ICONSTOP );
		return FALSE;
	}

	return TRUE;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

void fn_vMakeSndParserCommandLine( char *szCommandLine )
{
	int       iCmdLineCount        = 0;

	iCmdLineCount = sprintf( 
		szCommandLine, 
		"%s\\rm2SndParser.exe -TableType:0 -Language:English -Anims:\"%s\\Gamedata\\World\\Graphics\\Anims\" -Anims:\"%s\\Gamedata\\World\\Levels\\_common\\families\"", 
		(LPCTSTR)g_stTheGlobalData.csStartupDirectory,
		(LPCTSTR)g_stTheGlobalData.csMainDirectory,
		(LPCTSTR)g_stTheGlobalData.csMainDirectory );

	// Add specific gamedata to command line
	if( g_stTheGlobalData.a_csSoundVersionDirectories.GetSize() > 0 )
	{
		iCmdLineCount += sprintf( szCommandLine+iCmdLineCount, 
			" -CommonGamedata:\"%s\\Gamedata\" -Gamedata:\"%s\\%s\" -LcbFile:\"%s\\%s\\World\\Sound\\Rayman2.lcb\"",
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.a_csSoundVersionDirectories[0],
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.a_csSoundVersionDirectories[0] );
	}
	else
	{
		iCmdLineCount += sprintf( szCommandLine+iCmdLineCount, 
			" -Gamedata:\"%s\\Gamedata\" -LcbFile:\"%s\\%s\\World\\Sound\\Rayman2.lcb\"",
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.csMainDirectory );
	}

	// Add specific anim paths to command line
	for( int i=0; i<g_stTheGlobalData.a_csVersionDirectories.GetSize(); i++ )
	{
		iCmdLineCount += sprintf( szCommandLine+iCmdLineCount, 
			" -Anims:\"%s\\%s\\World\\Graphics\\Anims\" -Anims:\"%s\\%s\\World\\Levels\\_common\\families\"",
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.a_csVersionDirectories[i],
			(LPCTSTR)g_stTheGlobalData.csMainDirectory,
			(LPCTSTR)g_stTheGlobalData.a_csVersionDirectories[i] );
	}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// compute short name of exe (without path and extension).
void fn_vGetShortExeName( char *_szShortExeName, char *_szLongExeName )
{
	int   iPathLength, iExeNameLength;

	if( (iPathLength = strrchr( _szLongExeName, '\\' ) - _szLongExeName) < 0 )
		iPathLength = -1;
	if( (iExeNameLength = strrchr( _szLongExeName, '.' ) - _szLongExeName - (iPathLength > 0 ? iPathLength : 0) - 1) > 0 )
	{
		strncpy( _szShortExeName, &_szLongExeName[iPathLength+1], iExeNameLength );
		_szShortExeName[iExeNameLength] = '\0';
	}
	else
		strcpy( _szShortExeName, &_szLongExeName[iPathLength+1] );
}