 // PatchMaker.cpp: implementation of the CPatchMaker class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "NeoPatchMaker.h"
#include "PatchMaker.h"
#include "Crc32Static.h"
#include "afxinet.h"
#include "set"


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif




//////////////////////////////////////////////////////////////////////////
//
CPatchMaker::CPatchMaker()
{
	m_PatchmakerState = PMS_IDLE;
}

CPatchMaker::~CPatchMaker()
{

}


bool CPatchMaker::Go( CEdit* pEditOutput )
{
	//////////////////////////////////////////////////////////////////////////
	// 변수 초기화
	m_ZipArc.Close();
	m_arrIndexToUpdateLocalFile.RemoveAll();
	m_LocalPatchInfo.Reset();
	m_ServerPatchInfo.Reset();
	m_strCurrentNewPackageFileName = "";

	m_astrFileNameWithSubDirExclusionList.RemoveAll();
	m_astrFileNameExclusionList.RemoveAll();
	m_astrSubDirExclusionList.RemoveAll();
	
	m_ZipArc.SetCaseSensitivity( false );

	

	//////////////////////////////////////////////////////////////////////////
	// 초기값 설정(Test only)
//	m_dwPackageMaxSize = 500 * 1024;	// 500kbytes, 0.5MB
//
//	m_strPackageFilePath = "F:\\FilePatchTest\\";
//	m_strLocalFilePath = "F:\\RYL\\Risk Your Life (Test Server Version)\\";
//	m_strDestFilePath = "F:\\FilePatchTest\\modified\\";
//	m_SourceType = FILE_ON_LOCAL;
//	m_DestType = FILE_ON_LOCAL;
//	m_dwUpdateVersion = 352;
//
//	m_strFTPServerAddr = "211.233.42.245";
//	m_strFTPAccount = "ryl";
//	m_strFTPPassword = "fldjzk!$";

	//////////////////////////////////////////////////////////////////////////
	// validity check (path seperator, FTP connectivity)
	pEditOutput->SetWindowText( "0. Check validity" );
	if( false == CheckValidity( pEditOutput ) )
	{
		pEditOutput->SetWindowText( "Faild (Check validity)" );
		return false;
	}


	//////////////////////////////////////////////////////////////////////////
	// server patch info 읽어들임
	m_PatchmakerState = PMS_LOAD_SERVER_PATCHINFO_LIST;
	pEditOutput->SetWindowText( "1. Loading previous version patchinfo list" );
	if( false == LoadServerPatchInfoList() )
	{
		pEditOutput->SetWindowText( "Faild (Loading previous version patchinfo list)" );
		m_PatchmakerState = PMS_IDLE;
		return false;
	}
	
	//////////////////////////////////////////////////////////////////////////
	// local patch info 작성
	m_PatchmakerState = PMS_BUILD_LOCAL_PATCHINFO_LIST;
	pEditOutput->SetWindowText( "2. Building local patchinfo list" );
	if( false == BuildLocalPatchInfoList() )
	{
		pEditOutput->SetWindowText( "Faild (Building local patchinfo list)" );
		m_PatchmakerState = PMS_IDLE;
		return false;
	}

	//////////////////////////////////////////////////////////////////////////
	// 기존 패키지 내 파일 중 업데이트 할 파일 & 새로 패키지화되어야 할 파일 목록 작성
	m_PatchmakerState = PMS_COLLECT_UPDATE_FILE_LIST;
	pEditOutput->SetWindowText( "3. Compare & collecting update file list" );
	if( false == CollectUpdateFileList() )
	{
		pEditOutput->SetWindowText( "Faild (Compare & collecting update file list)" );
		m_PatchmakerState = PMS_IDLE;
		return false;
	}
	
	//////////////////////////////////////////////////////////////////////////
	// 패키지 파일 업데이트 & 신규 패키지 작성
	m_PatchmakerState = PMS_UPDATE_PACKAGE;
	pEditOutput->SetWindowText( "4. Updating & creating package(.zip) file" );
	if( false == UpdatePackage() )
	{
		pEditOutput->SetWindowText( "Faild (Updating & creating package(.zip) file)" );
		m_PatchmakerState = PMS_IDLE;
		return false;
	}

	//////////////////////////////////////////////////////////////////////////
	// PatchInfo 파일 저장&압축
	m_PatchmakerState = PMS_UPDATE_PATCHINFO;
	pEditOutput->SetWindowText( "5. Updating patchinfo file" );
	if( false == UpdatePatchInfo() )
	{
		pEditOutput->SetWindowText( "Faild (Updating patchinfo file)" );
		m_PatchmakerState = PMS_IDLE;
		return false;
	}

	//////////////////////////////////////////////////////////////////////////
	// Clear
	pEditOutput->SetWindowText( "-- Finished --" );

	
	return true;
}


//////////////////////////////////////////////////////////////////////////
// validity check (path seperator, FTP connectivity)
bool CPatchMaker::CheckValidity( CEdit* pEditOutput )
{
	// 이전버전 패키지 디렉토리명 끝의 seperator 확인
	if( m_strPackageFilePath.GetLength() )
	{
		if( FILE_ON_FTP == m_SourceType )
		{
			if( m_strPackageFilePath.Right(1) != "/" )
			{
				m_strPackageFilePath += "/";
			}
		}

		if( FILE_ON_LOCAL == m_SourceType )
		{
			if( m_strPackageFilePath.Right(1) != "\\" )
			{
				m_strPackageFilePath += "\\";
			}
		}
	}
	else
	{
		return false;
	}

	if( m_strLocalFilePath.GetLength() )
	{
		if( m_strLocalFilePath.Right(1) != "\\" )
		{
			m_strLocalFilePath += "\\";
		}
	}
	else
	{
		return false;
	}

	if( m_strDestFilePath.GetLength() )
	{
		if( FILE_ON_FTP == m_DestType )
		{
			if( m_strDestFilePath.Right(1) != "/" )
			{
				m_strDestFilePath += "/";
			}
		}
		
		if( FILE_ON_LOCAL == m_DestType )
		{
			if( m_strDestFilePath.Right(1) != "\\" )
			{
				m_strDestFilePath += "\\";
			}
		}
	}
	else
	{
		return false;
	}

	BOOL bFTPSettingOK = TRUE;
	if( FILE_ON_FTP == m_SourceType )
	{
		CInternetSession isess;
		CFtpConnection* pFtp = isess.GetFtpConnection( m_strFTPServerAddr, m_strFTPAccount, m_strFTPPassword, m_nFTPPort, m_bPassiveMode );
		bFTPSettingOK = pFtp->SetCurrentDirectory( m_strPackageFilePath );
		pFtp->Close();
		isess.Close();
	}

	if( FALSE == bFTPSettingOK )
	{
		return false;
	}
	
	return true;
}




//////////////////////////////////////////////////////////////////////////
// server patch info 읽어들임
bool CPatchMaker::LoadServerPatchInfoList()
{
	switch( m_SourceType )
	{
	case FILE_NONE:
		return true;
		break;

	case FILE_ON_FTP:
		{
			CInternetSession isess;
			CFtpConnection* pFtp = isess.GetFtpConnection( m_strFTPServerAddr, m_strFTPAccount, m_strFTPPassword, m_nFTPPort, m_bPassiveMode);
			pFtp->SetCurrentDirectory( m_strPackageFilePath );
			pFtp->GetFile( "PatchInfoV2.zip", m_strDestFilePath+"PatchInfoV2.zip" );
			pFtp->Close();
			isess.Close();
		}
		break;

	case FILE_ON_LOCAL:
		CopyFile( m_strPackageFilePath+"PatchInfoV2.zip", m_strDestFilePath+"PatchInfoV2.zip", FALSE );
		break;
	}

	// PatchInfo 압축해제후 압축파일 삭제
	CZipArchive zipArc;
	zipArc.Open( m_strDestFilePath+"PatchInfoV2.zip", CZipArchive::zipOpen );
	zipArc.ExtractFile( 0, m_strDestFilePath, false, "PatchInfoV2" );
	zipArc.Close();
	DeleteFile( m_strDestFilePath+"PatchInfoV2.zip" );

	// PatchInfo 로드
	m_ServerPatchInfo.Reset();
	m_ServerPatchInfo.Load( m_strDestFilePath+"PatchInfoV2" );
	DeleteFile( m_strDestFilePath+"PatchInfoV2");


	// filename-to-index map 생성
	for( size_t i = 0; i < m_ServerPatchInfo.GetList().size(); ++i )
	{
		long lTmp =  m_mapFileName2ServerListIndex.size();
		m_mapFileName2ServerListIndex[ m_ServerPatchInfo.GetList()[i].szFileName ] = lTmp;
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////
// local patch info 작성
bool CPatchMaker::BuildLocalPatchInfoList()
{
	WIN32_FIND_DATA w32fd;

	HANDLE hFind = FindFirstFile( m_strLocalFilePath+"*.*", &w32fd );

	if( INVALID_HANDLE_VALUE == hFind )
	{
		return false;
	}

	CString strCurrentSubPath("");
	CStringArray astrSubDirList;
	while( 1 )
	{
		if( !IsExcludedFileWithSubDir(strCurrentSubPath+w32fd.cFileName) &&
			!IsExcludedFile( w32fd.cFileName ) )
		{
			if( w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
			{
				CString strTmp;
				strTmp.Format( "%s%s", strCurrentSubPath, w32fd.cFileName );
				if( !IsExcludedSubDir( strTmp ) )
				{
					astrSubDirList.Add( strTmp );
				}
			}
			else
			{
				FilePatchInfoRecord fpir;


				strcpy( fpir.szFileName, strCurrentSubPath+w32fd.cFileName );
				CString strFullPathName = m_strLocalFilePath+strCurrentSubPath+w32fd.cFileName;

				m_strStateInfoString.Format( "새 버전의 %s 파일을 검사중", strFullPathName );

				CCrc32Static::FileCrc32Assembly( strFullPathName, fpir.dwCRC32 );
				HANDLE hFile = CreateFile( strFullPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
					NULL, OPEN_EXISTING, 0, 0 );
				fpir.dwSize = GetFileSize ( hFile, NULL );
				CloseHandle( hFile );

				m_LocalPatchInfo.GetList().push_back( fpir );

				//m_mapFileName2LocalListIndex[fpir.szFileName] = m_LocalPatchInfo.GetList().size() - 1;
			}
		}

		if( 0 == FindNextFile( hFind, &w32fd ) )
		{
			FindClose( hFind );
	

			// sub dir queue 에 순서를 기다리고 있는 놈이 존재하면
			if( astrSubDirList.GetSize() > 0 )
			{
				// 꺼내서 쓰고
				strCurrentSubPath = astrSubDirList[0] + "\\";
				astrSubDirList.RemoveAt(0);
				hFind = FindFirstFile( m_strLocalFilePath+strCurrentSubPath+"*.*", &w32fd );
			}
			else
			{
				// 없음 루프 종료
				break;
			}
		}
	}
	
	return true;
}


//////////////////////////////////////////////////////////////////////////
// 기존 패키지 내 파일 중 업데이트 할 파일 & 새로 패키지화되어야 할 파일 목록 작성
bool CPatchMaker::CollectUpdateFileList()
{
	size_t i = 0, max = m_LocalPatchInfo.GetList().size();

	for( i = 0; i < max; ++i )
	{
		FilePatchInfoRecord &rLocalItem = m_LocalPatchInfo.GetList()[i];
		
//		bool bFound = false;
//		for( j = 0; j < m_ServerPatchInfo.GetList().size(); ++j )
//		{
//			FilePatchInfoRecord &rServerItem = m_ServerPatchInfo.GetList()[j];
//			
//			// 기존 패키지에 존재하는 파일일 경우(로컬 파일이 서버 리스트에서 발견된 경우)
//			if( 0 == stricmp( rLocalItem.szFileName, rServerItem.szFileName ) )
//			{
//				bFound = true;
//				strcpy( rLocalItem.szPackageFileName, rServerItem.szPackageFileName );
//				
//				// size와 CRC32 체크해 다르면 업데이트 대상에 등록
//				if( rLocalItem.dwCRC32 != rServerItem.dwCRC32 ||
//					rLocalItem.dwSize != rServerItem.dwSize )
//				{
//					m_arrIndexToUpdateLocalFile.Add( i );
//
//					rLocalItem.dwVersion = m_dwUpdateVersion;
//				}
//				else
//				{
//					rLocalItem.dwVersion = rServerItem.dwVersion;
//				}
//				
//				break;
//			}
//			
//		}
//		
//		// 기존 패키지에 존재하지 않는 파일
//		if( false == bFound )
//		{
//			m_arrIndexToUpdateLocalFile.Add( i );
//
//			rLocalItem.dwVersion = m_dwUpdateVersion;
//		}

		std::map< PTR_STRING, int, PTR_STRING_LESS >::iterator it;
		it = m_mapFileName2ServerListIndex.find( rLocalItem.szFileName );
		if( m_mapFileName2ServerListIndex.end() == it )
		{
			// 기존 패키지에 존재하지 않는 파일
			m_arrIndexToUpdateLocalFile.Add( i );

			rLocalItem.dwVersion = m_dwUpdateVersion;
		}
		else
		{
			long lIdx = it->second;
			FilePatchInfoRecord &rServerItem = m_ServerPatchInfo.GetList()[it->second];

			strcpy( rLocalItem.szPackageFileName, rServerItem.szPackageFileName );
			
			// size와 CRC32 체크해 다르면 업데이트 대상에 등록
			if( rLocalItem.dwCRC32 != rServerItem.dwCRC32 ||
				rLocalItem.dwSize != rServerItem.dwSize )
			{
				m_arrIndexToUpdateLocalFile.Add( i );

				rLocalItem.dwVersion = m_dwUpdateVersion;
			}
			else
			{
				rLocalItem.dwVersion = rServerItem.dwVersion;
			}
		}	
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 패키지 파일 업데이트 & 신규 패키지 작성
bool CPatchMaker::UpdatePackage()
{
	int i;

	std::set<CString> setUpdatePackageFileList;
	
	for( i = 0; i < m_arrIndexToUpdateLocalFile.GetSize(); ++i )
	{
		FilePatchInfoRecord &rFpir = m_LocalPatchInfo.GetList()[ m_arrIndexToUpdateLocalFile[i] ];
		
		if( '\0' != rFpir.szPackageFileName[0] )	// 패키지 파일 이름이 빈 문자열이 아닐 경우(이미 패키지에 들어가 있는 파일)
		{
			// 받아올 패키지 파일 목록에 이미 등록되어있는지 확인(없으면 추가)
			setUpdatePackageFileList.insert( CString(rFpir.szPackageFileName) );
		}
	}
	
	// 패키지 파일 복사(혹은 다운로드)
	std::set<CString>::iterator it;
	switch( m_SourceType )
	{
	case FILE_ON_LOCAL:
		{
			// copy packages from src to dest
			for(it = setUpdatePackageFileList.begin();
				it != setUpdatePackageFileList.end(); ++it )
			{
				CString &rstr = *it;
				CopyFile( m_strPackageFilePath+(*it)+".zip", m_strDestFilePath+(*it)+".zip", FALSE );
			}
		}
		break;
		
	case FILE_ON_FTP:
		{
			CInternetSession isess;
			CFtpConnection* pFtp = isess.GetFtpConnection( m_strFTPServerAddr, m_strFTPAccount, m_strFTPPassword, m_nFTPPort, m_bPassiveMode );
			pFtp->SetCurrentDirectory( m_strPackageFilePath );
			for(it = setUpdatePackageFileList.begin(); it != setUpdatePackageFileList.end(); ++it )
			{
				pFtp->GetFile( (*it)+".zip", m_strDestFilePath+(*it)+".zip" );
				Sleep(1);
			}
			pFtp->Close();
			isess.Close();
		}
		break;
	}


	// package update, 혹은 신규 package 작성
	for( i = 0; i < m_arrIndexToUpdateLocalFile.GetSize(); ++i )
	{
		Sleep(1);

		FilePatchInfoRecord &rFpir = m_LocalPatchInfo.GetList()[ m_arrIndexToUpdateLocalFile[i] ];

		if( '\0' == rFpir.szPackageFileName[0] )	
		{
			// 파일의 패키지 이름 필드가 비어 있으면 -> 신규 or 마지막 패키지에 추가
			//
			// 주의 : 패키지 이름 필드가 비어 있다고 해서 항상 신규 패키지를 만드는게 아니다.
			// 최근에 만든 신규 패키지가 다 채워지지 않았으면 거기에 들어가는 것임

			if( m_strCurrentNewPackageFileName == "" )
			{
				// 패키지 파일을 신규로 만들거나 
				m_strCurrentNewPackageFileName = GetNextPackageFileName();
				m_ZipArc.Open( m_strDestFilePath+m_strCurrentNewPackageFileName+".zip", CZipArchive::zipCreate );
			}
			else
			{
				// granular 용량이 다 채워지지않은 패키지가 마지막일 경우 그것을 오픈
				m_ZipArc.Open( m_strDestFilePath+m_strCurrentNewPackageFileName+".zip", CZipArchive::zipOpen );
			}

			m_ZipArc.SetRootPath( m_strLocalFilePath );
			CZipAddNewFileInfo zanfi( m_strLocalFilePath+rFpir.szFileName, false );
			zanfi.Defaults();
			m_ZipArc.AddNewFile( zanfi );
			m_ZipArc.Close();
			// patchinfo 에 기록될 패키지 이름 필드 업데이트
			strcpy( rFpir.szPackageFileName, m_strCurrentNewPackageFileName );

			DWORD dwPackageSize = GetFileSize( m_strDestFilePath+m_strCurrentNewPackageFileName+".zip" );

			// 패키지 최대 사이즈를 넘어갔을 경우 or 단독패키지화 해야 할 파일(Client.exe 같은) 일 경우
			bool bSoleFile = IsSoleFileWithSubDir( rFpir.szFileName );
			if( dwPackageSize > m_dwPackageMaxSize || bSoleFile )
			{
				m_ZipArc.Open( m_strDestFilePath+m_strCurrentNewPackageFileName+".zip", CZipArchive::zipOpen );
				if( 1 == m_ZipArc.GetCount( true ) )
				{
					// 파일이 하나뿐일경우 어쩔수 없으므로 패스
					m_ZipArc.Close();
				}
				else
				{
					// 파일이 여러개일 경우, 마지막으로 추가된 파일 삭제
					int nIdx = m_ZipArc.FindFile( rFpir.szFileName, CZipArchive::ffCaseSens, false );
					m_ZipArc.DeleteFile( nIdx );
					m_ZipArc.Close();

					// ...한 다음, 다음 패키지 파일을 생성해 거기에 냅다 추가
					m_strCurrentNewPackageFileName = GetNextPackageFileName();
					m_ZipArc.Open( m_strDestFilePath+m_strCurrentNewPackageFileName+".zip", CZipArchive::zipCreate );
					m_ZipArc.SetRootPath( m_strLocalFilePath );
					CZipAddNewFileInfo zanfi( m_strLocalFilePath+rFpir.szFileName, false );
					zanfi.Defaults();
					m_ZipArc.AddNewFile( zanfi );
					m_ZipArc.Close();
					// patchinfo 에 기록될 패키지 이름 필드 업데이트
					strcpy( rFpir.szPackageFileName, m_strCurrentNewPackageFileName );
				}

				// 단독패키지화 해야 할 파일의 경우 이후에 추가되는 파일이 없어야한다.
				// (current패키지 이름을 비움 -> 다음 턴에 새 파일을 만들게됨)
				if( bSoleFile )
				{
/*
					m_strCurrentNewPackageFileName = GetNextPackageFileName();
					m_ZipArc.Open( m_strDestFilePath+m_strCurrentNewPackageFileName+".zip", CZipArchive::zipCreate );
					m_ZipArc.SetRootPath( m_strLocalFilePath );
					m_ZipArc.Close();
*/
					m_strCurrentNewPackageFileName = "";
				}

			}

			//strcpy( rFpir.szPackageFileName, m_strCurrentNewPackageFileName );
		}
		else
		{
			// 기존패키지 업데이트(replace file)
			m_ZipArc.Open( m_strDestFilePath+rFpir.szPackageFileName+".zip", CZipArchive::zipOpen );
			
			int nIdx = m_ZipArc.FindFile( rFpir.szFileName, CZipArchive::ffCaseSens, false );
			if( -1 == nIdx )
			{
				CString strErr;
				strErr.Format( "%s 패키지 파일 내에 %s 파일이 존재하지 않구마이", rFpir.szPackageFileName, rFpir.szFileName );
				AfxMessageBox( strErr );
			}
			else
			{
				m_ZipArc.DeleteFile( nIdx );
			}
			
			m_ZipArc.SetRootPath( m_strLocalFilePath );
			CZipAddNewFileInfo zanfi( m_strLocalFilePath+rFpir.szFileName, false );
			zanfi.Defaults();
			m_ZipArc.AddNewFile( zanfi );
			
			m_ZipArc.Close( CZipArchive::afNoException, true );
		}
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////
// PatchInfo 파일 저장&압축
bool CPatchMaker::UpdatePatchInfo()
{
	// PatchInfo 저장
	m_LocalPatchInfo.SetVersion( m_dwUpdateVersion );
	m_LocalPatchInfo.Save( m_strDestFilePath+"PatchInfoV2" );	

	// PatchInfo 압축후 원본파일 삭제
	CZipArchive zipArc;
	zipArc.Open( m_strDestFilePath+"PatchInfoV2.zip", CZipArchive::zipCreate );
	zipArc.AddNewFile( m_strDestFilePath+"PatchInfoV2", "PatchInfoV2" );
	zipArc.Close();

	DeleteFile( m_strDestFilePath+"PatchInfoV2");
	
//	CInternetSession isess;
//	CFtpConnection* pFtp = isess.GetFtpConnection( "211.233.42.245", "ryl", "fldjzk!$", 21, m_bPassiveMode );
//	pFtp->SetCurrentDirectory( m_strPackageFilePath );
//	pFtp->GetFile( "PatchInfo.zip", m_strDestFilePath+"PatchInfo.zip" );
//	pFtp->Close();
//	isess.Close();

	return true;
}


//////////////////////////////////////////////////////////////////////////
//
// 새 패키지 파일 이름 생성
// (파일 이름 중 확장자 제외)
const char* CPatchMaker::GetNextPackageFileName()
{
	static char szBuff[MAX_PACKAGE_FILE_NAME_LENGTH*2];

	CTime t = CTime::GetCurrentTime();

	CString strHeader = t.Format( "%Y%m%d_%H%M_" );

	int n = 0;
	char szTmp[4];
	while( n < 1000 )
	{
		sprintf( szTmp, "%03d", n );
		CString strFileName =  m_strDestFilePath + strHeader + szTmp + ".zip";

//		if( 0xFFFFFFFF == GetFileAttributes( strFileName ) )
//		{
//			if( ERROR_FILE_NOT_FOUND == GetLastError() )
//			{
//				strcpy( szBuff, strHeader+szTmp );
//				break;				
//			}
//		}
		if( !IsFileExist( strFileName ) )
		{
			strcpy( szBuff, strHeader+szTmp );
			break;				
		}

		++n;
	}

	if( 1000 == n )
	{
		return NULL;
	}

	return szBuff;
}


bool CPatchMaker::IsExcludedFile( const char* szFileName )
{
	for( int i = 0; i < m_astrFileNameExclusionList.GetSize(); ++i )
	{
		if( 0 == stricmp( szFileName, m_astrFileNameExclusionList[i] ) )
		{
			return true;
		}
	}

	// hard-coded exclusion list
	if( 0 == stricmp( szFileName, "." ) ) return true;
	if( 0 == stricmp( szFileName, ".." ) ) return true;
	if( 0 == stricmp( szFileName, "vssver.scc" ) ) return true;

	return false;
}


bool CPatchMaker::IsExcludedFileWithSubDir( const char* szFileNameWithSubDir )
{
	for( int i = 0; i < m_astrFileNameWithSubDirExclusionList.GetSize(); ++i )
	{
		if( 0 == stricmp( szFileNameWithSubDir, m_astrFileNameWithSubDirExclusionList[i] ) )
		{
			return true;
		}
	}

	// hard-coded exclusion list
	if( 0 == stricmp( szFileNameWithSubDir, "mp.ini" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "RenderOption.dat" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "ClientOption.dat" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "Patchinfo.pat" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "UpdateFileList.txt" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "VersionInfo.dat" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "Login.dat" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "LoginRes.dll" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "Patchinfo.cache" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "Patchinfo.local" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "LC2.OGG" ) ) return true;

	if( 0 == stricmp( szFileNameWithSubDir, "Z3D_SetupDirectX.exe" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "DirectXInstaller.exe" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "FileChecker.exe" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "DSETUP.dll" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "dsetup32.dll" ) ) return true;

	return false;
}


bool CPatchMaker::IsExcludedSubDir( const char* szSubDir )
{
	for( int i = 0; i < m_astrSubDirExclusionList.GetSize(); ++i )
	{
		if( 0 == stricmp( szSubDir, m_astrSubDirExclusionList[i] ) )
		{
			return true;
		}
	}
	
	// hard-coded exclusion list
	//if( 0 == stricmp( szSubDir, "GameGuard" ) ) return true;
	if( !m_bCheckNoticeFolder && 0 == stricmp( szSubDir, "Notice" ) ) return true;

	//if( 0 == stricmp( szSubDir, "update" ) ) return true;
	if( 0 == stricmp( szSubDir, "DirectX" ) ) return true;

	return false;
}


// 단일 패키지로 만들어야 하는 파일인가?
bool CPatchMaker::IsSoleFileWithSubDir( const char* szFileNameWithSubDir )
{
//	for( int i = 0; i < m_astrSubDirSoleFileList.GetSize(); ++i )
//	{
//		if( 0 == stricmp( szSubDir, m_astrSubDirSoleFileList[i] ) )
//		{
//			return true;
//		}
//	}

	// hard-coded sole file list
	if( 0 == stricmp( szFileNameWithSubDir, "Client.exe" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "Login.new" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "ItemScript.gsf" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "MonsterProtoType.gsf" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "script1.gsf" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "SkillScript.gsf" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "npcscript.mcf" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "quest.mcf" ) ) return true;
	if( 0 == stricmp( szFileNameWithSubDir, "Script.mcf" ) ) return true;

//	if( 0 == stricmp( szSubDir, "" ) ) return true;


	return false;
}


int CPatchMaker::GetPatchInfoVersion()
{
	if( false == LoadServerPatchInfoList() )
	{
		return -1;
	}

	return (int)m_ServerPatchInfo.GetVersion();
}