#include "stdafx.h"
#include "Patcher.h"

#include "FileFinder.h"
#include "FileUnzipper.h"

#include <tlhelp32.h>

#ifdef _DEBUG
#define DebugPrint	printf
#else
#define DebugPrint	//
#endif

#define PACK_PASSWORD	"dkdlfltmehdwjq10aks"

/// [12/9/2008 Jo] loader.dll 관련 함수 추가
/*-- Local definitions--*/
#define SafeDelete(P)	{ if (P!=NULL) { delete(P); (P)=NULL; } }       // 메모리를 안전하게 해제.

typedef int			(LibNumberClasses)	(void);
typedef ClassDesc*	(LibClassDesc)		(int i);
typedef ULONG		(LibVersion)		(void);


/*-- Global data--*/
LibVersion*       g_LibVersion       = NULL; 
LibNumberClasses* g_LibNumberClasses = NULL;
LibClassDesc*     g_LibClassDesc     = NULL;

DWORD g_saveTime=0;
DWORD g_createTime =0;
DWORD g_addTime = 0;
DWORD g_totalTime = 0;


cPatcher::cPatcher( cPatcherDlg* dlg )
: mDlg( dlg )
, mWnd( 0 )
, mNumPatchFiles( 0 )
, mEndApp( false )
, mEndIO( true )
{
	m_packlist.pack = NULL;
	m_patchlist.file = NULL;
}

cPatcher::~cPatcher()
{
	Clear();
	m_compressedFileList.ReleaseMemory();
	m_UnCompressedFileList.ReleaseMemory();
	SafeDelete( m_libClassDesc );
	m_libClassDesc = NULL;
}

void cPatcher::Clear()
{
	mNumPatchFiles = 0;
}

void cPatcher::StopWorking()
{
	mEndApp = true;
	mDlg = NULL;
}

HANDLE cPatcher::GetProcessList( bool* bFuncFailed )
{
	HANDLE hProcessSnap;
	PROCESSENTRY32 pe32;

	// Take a snapshot of all processes in the system.
	hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
	if( hProcessSnap == INVALID_HANDLE_VALUE )
	{
		//printError( "CreateToolhelp32Snapshot (of processes)" );
		*bFuncFailed = true;
		return NULL;
	}

	// Set the size of the structure before using it.
	pe32.dwSize = sizeof( PROCESSENTRY32 );

	// Retrieve information about the first process,
	// and exit if unsuccessful
	if( !Process32First( hProcessSnap, &pe32 ) )
	{
		//printError( "Process32First" );  // Show cause of failure
		CloseHandle( hProcessSnap );     // Must clean up the snapshot object!
		*bFuncFailed = true;
		return NULL;
	}

	// Now walk the snapshot of processes, and
	// display information about each process in turn
	do
	{
		if( stricmp( pe32.szExeFile, LAUNCHER_FILENAME) == 0 )
			return OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );

	} while( Process32Next( hProcessSnap, &pe32 ) );

	CloseHandle( hProcessSnap );
	return NULL;
}


DWORD cPatcher::ThreadProc()
{
	assert( mWnd );

	bool   bFuncFailed = false;
	HANDLE hLauncherProcess = NULL;

	hLauncherProcess = GetProcessList( &bFuncFailed );

	if( bFuncFailed == true )
	{
		EventWrite("Thread:Failed to scan processers. ");
		::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_INIT_, (LPARAM)0);
		return 0;
	}

	if( hLauncherProcess != NULL )
	{
		if ( WaitForSingleObject( hLauncherProcess, 5000 ) != WAIT_OBJECT_0 )
		{
			DWORD exitCode = 0;

			GetExitCodeThread( hLauncherProcess, &exitCode );
			if ( exitCode == STILL_ACTIVE )
			{
				TerminateProcess(hLauncherProcess, exitCode);
			}
		}
		CloseHandle( hLauncherProcess );
		hLauncherProcess = NULL;
	}


	g_totalTime = GetTickCount();

	Clear();

	char hostaddr[ 1024 ];
	char hosturl[ 1024 ];
	char hostlist[ 1024 ];

	if( mInstance == NULL )
	{
		EventWrite("Thread:Instance is NULL. ");
		::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_INIT_, (LPARAM)0);
		return 0;
	}

	LoadString( mInstance, IDS_UPDATE_HOST, hostaddr, sizeof(hostaddr) );
	LoadString( mInstance, IDS_UPDATE_URL,  hosturl,  sizeof(hosturl) );
	LoadString( mInstance, IDS_UPDATE_LIST, hostlist, sizeof(hostlist) );

	if( m_libClassDesc->Open( hostaddr ) ) 
	{
		EventWrite("Thread:Failed to connect to patch server. ");
		::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_DOWNLOAD_, (LPARAM)0);
		return 0;
	}

	char  url[ 1024 ];
	char* buffer    = NULL;
	DWORD bufferLen = 0;
	DWORD exitCode  = 0;
	DWORD retValue  = 0;

	SetProgressMax( PATCH_SEARCHING, 0 );

	sprintf(url, "%s/%s", hosturl, hostlist);

	retValue = m_libClassDesc->Get( url, &LoaderProgressRoutine, &exitCode, this, &buffer, &bufferLen );
	
	/// 패치리스트 다운로드 및 파싱
	if ( buffer != NULL && bufferLen > 0 && retValue == 0)
	{
		if( UnpackPatchList( &buffer, &bufferLen ) == false ) 
		{
			::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_SAVE_, (LPARAM)0);
			return 0;
		}
		/// 패치리스트 파싱
		bool ismake = MakeList( buffer );
		GlobalFree( buffer );
		if ( ismake == false )
		{
			EventWrite("Thread:Failed to parse server patch list file. ");
			::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_PARSE_, (LPARAM)0);
			return 0;
		}
	}
	else
	{
		EventWrite("Thread:Failed to download server patch list file. ");
		::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_DOWNLOAD_, (LPARAM)0);
		return 0;
	}
	
 	int  ifiles;
 	int  idirectories;
 	 	
	/// 클라이언트 파일 정보 수집
	if ( Search( m_currentDirectory, NULL, ifiles=0, idirectories=0, true ) == -1 )
	{
		EventWrite("Thread:Failed to search client files.");
		::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_SEARCH_, (LPARAM)0);
		return 0;
	}

	/// 폴더 생성
	Packfile* pack = m_packlist.pack;
	for ( ULONG i = 0; i < m_packlist.length && mEndApp == false ; i++, pack++ )
	{
		char     makeDir[ 1024 ];
		char     foldername[ 1024 ];
		char    *token = NULL;
		const char	seps[] = "/\\";
		bool     makeSubDirectory = false;
		
		strncpy( foldername, pack->directory , strlen(pack->directory)+1 );

		token = strtok( foldername, seps );

		/// 압축해서 넣지 않는 폴더들은 하위 폴더까지 생성한다. (ex : sound 폴더와 launcher 폴더 )
		for(unsigned int index = 0; index < UNPACK_FOLDER_COUNT; ++index )
		{
			if( stricmp( token, gUnpackFolder[index] ) == 0 )
				makeSubDirectory = true;
		}

		sprintf( makeDir, "%s\\", m_currentDirectory );

		while ( token != NULL )
		{
			if( mEndApp == true )
				return 0L;
			
			strcat( makeDir, token );
			strcat( makeDir, "\\" );

			if ( GetFileAttributes( makeDir ) == INVALID_FILE_ATTRIBUTES )
			{
				if ( CreateDirectory( makeDir, NULL ) == FALSE )
				{
					EventWrite("Thread:Fail to Create Client Folder. ");
					::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_CREATE_FOLDER_, (LPARAM)0);
					return 0L;
				}
			}
			token = strtok( NULL, seps );

			if( makeSubDirectory == false )
				break;
		}
	}

	/// 패치리스트 목록을 로컬디스크에 저장된 데이터와 비교하여 업데이트 리스트 목록 작성
	Patchfile* files = m_patchlist.file;
 	for ( ULONG i = 0; i < m_patchlist.length && mEndApp == false ; i++, files++ )
 	{
 		if ( VerifyFile( files ) == false )
 		{
			/// 파일을 바로 다운로드 하지 않고 리스트에 적제 후 나중에 하나씩 받는다.( 패치 진행도(%)를 보여주고 위해서 작업량을 체크해야 함)
			++mNumPatchFiles;
			m_updateList.push_back( (*files) );
		}
	}

	///메모리 해제
	if( m_compressedFileList.ReleaseMemory() == false || m_UnCompressedFileList.ReleaseMemory() == false )
	{
		EventWrite("Thread:Fail to Release client list Memory. ");
		::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_MEMORY_FREE_, (LPARAM)0);
		return 0;
	}	

	/// 업데이트 해야할 파일의 개수로 프로그래스바 값 설정.
	SetProgressMax( PATCH_PATCHING, mNumPatchFiles);
	int progPos = 0;
	buffer = NULL;
	bufferLen = 0;

	for( vector<Patchfile>::iterator iter = m_updateList.begin() ; iter != m_updateList.end() && mEndApp == false; ++iter )
	{
		sprintf( url, "%s/%s.z", hosturl, iter->fileName );
  		exitCode = LPR_CONTINUE;

  		retValue = m_libClassDesc->Get( url, &LoaderProgressRoutine, &exitCode, this, &buffer, &bufferLen );
		
		try	{
			
			/// 중간에 접속이 재설정 되었다면
			if( retValue == ERROR_INTERNET_CONNECTION_RESET )
			{
				int retry = 0;   
				
				do
				{
					/// 재 접속
					if( m_libClassDesc->Open( hostaddr ) ) 
					{
						EventWrite("Thread:Failed to connect to patch server. ");
						::PostMessage(mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_DOWNLOAD_, (LPARAM)0);
						return 0;
					}

					retValue = m_libClassDesc->Get( url, &LoaderProgressRoutine, &exitCode, this, &buffer, &bufferLen );
					++retry;
					if( (retValue != ERROR_INTERNET_CONNECTION_RESET && retValue != 0) || retry == 3 ) 
						throw( 0 );

				} while ( retry < 3 && retValue != 0 );
			}
			
			if ( buffer != NULL && bufferLen > 0 && mEndApp == false && retValue == 0 )
			{
				if( UpdateFile( &(*iter) , buffer, bufferLen ) == false )
					throw( 0 );
			}
			else 
				throw( 0 );

		} catch ( ... )	{
			EventWrite("Thread:Fail to download [%s] file.", iter->fileName  );
			PostMessage( mWnd, WM_USER_MESSAGE, (WPARAM)_UMT_ERROR_DOWNLOAD_, (LPARAM)0 );
			return 0;
		}

		if( buffer != NULL )
  			GlobalFree( buffer );
		/// 진행바
		SetProgressPos( 0, ++progPos );
 	}
 
 	m_libClassDesc->Close( );

	printf("Total Time to Unpack and save buffer time is [%d]\n", g_saveTime );
	printf("Total End Create file...during time is [%d]\n", g_createTime );
	printf("Total End Add file...during time is [%d]\n", g_addTime );
	printf("Total Checking time [%d]\n", (GetTickCount() - g_totalTime));	

	SetProgressMax( PATCH_COMPLETE, 0 );

 	Sleep( 500 );

 	// 업데이트 완료.
	if( mDlg != NULL && mEndApp == false )
		mDlg->EndThread( true, false );

 	return 0L;

}


/*-- MakeList Method
*/
bool cPatcher::MakeList(char* buffer)
{
	// 메모리 토큰.
	char  seps[] = "\r\n/;";
	char* token;

	// Header
	token = strtok( (char*)buffer, seps );
	if ( token == NULL )
		return false;

	if ( strcmp( token, "patchlist" ) )
		return false;

	token = strtok( NULL, seps );
	if ( token == NULL )
		return false;

	if ( strcmp( token, "ver.110" ) )
		return false;

	token = strtok( NULL, seps );
	if ( token == NULL )
		return false;

	m_packlist.offset  = 0;
	m_packlist.length  = atoi( token );
	m_packlist.pack    = (Packfile*)GlobalAlloc( GPTR, sizeof(Packfile) * m_packlist.length );

	if( m_packlist.pack == NULL )
		return false;

	token = strtok( NULL, seps );
	if ( token == NULL )
		return false;

	m_patchlist.offset = 0;
	m_patchlist.length = atoi( token );
	m_patchlist.file   = (Patchfile*)GlobalAlloc( GPTR, sizeof(Patchfile) * m_patchlist.length );

	if( m_patchlist.file == NULL )
		return false;

	// Body
	Packfile* packfile = m_packlist.pack;
	for ( ULONG i = 0; i < m_packlist.length && mEndApp == false; i++, packfile++ )
	{
		// 폴더이름
		if ( (token = strtok( NULL, seps )) == NULL )
			return false;
		strcpy( packfile->directory, token );
	}

	Patchfile* patchfile = m_patchlist.file;
	for ( ULONG i = 0; i < m_patchlist.length && mEndApp == false; i++, patchfile++ )
	{
		// 파일이름
		if ( (token = strtok( NULL, seps )) == NULL )
			return false;
		strcpy( patchfile->fileName, token );

		// 파일크기-Low
		if ( (token = strtok( NULL, seps )) == NULL )
			return false;
		patchfile->sizeLow = (DWORD)atoi( token );

		// 파일크기-High
		if ( (token = strtok( NULL, seps )) == NULL )
			return false;
		patchfile->sizeHigh = (DWORD)atoi( token );

		// 파일날짜-Low
		if ( (token = strtok( NULL, seps )) == NULL )
			return false;
		patchfile->writeTime.dwLowDateTime = (DWORD)atoi( token );

		// 파일날짜-High
		if ( (token = strtok( NULL, seps )) == NULL )
			return false;
		patchfile->writeTime.dwHighDateTime = (DWORD)atoi( token );
	}

	if(mEndApp == true)
		return false;

	return true;
}

/*-- GetString Prototype --*/
int cPatcher::GetString(int id, char* buffer, int bufferSize )
{
	return ( mInstance != NULL) ? LoadString( mInstance, id, buffer, bufferSize ) : 0;
}

/*-- LoaderProgressRoutine Method --*/
DWORD cPatcher::LoaderProgressRoutine(DWORD totalFileSize, DWORD totalBytesTransferred, void* data)
{
	if( totalFileSize == totalBytesTransferred ) //파일 전송을 다 했다면
		return 1;	
	
	return 0;
	//Launcher* launcher = (Launcher*)data;
	//return launcher->LoaderProgress( totalFileSize, totalBytesTransferred, data );
}


/*-- FileTimeToDOS Method --*/
void cPatcher::FileTimeToDOS(FILETIME* filetime)
{
	WORD fatDate, fatTime;
	FileTimeToDosDateTime( filetime, &fatDate, &fatTime );
	DosDateTimeToFileTime( fatDate, fatTime, filetime );
}



/*-- UnpackPatchList Method --*/
bool cPatcher::UnpackPatchList( char** buffer, DWORD *bufferLen )
{
	char   hostlist[1024];
	char   packname[1024];
	HANDLE file;
	DWORD  written=0;
	BOOL   ret=FALSE;

	GetString( IDS_UPDATE_LIST, hostlist, sizeof(hostlist) );
	sprintf( packname, "%s", hostlist );

	mEndIO = false;
	/// 다운 받은 patchlist.txt.z파일 저장.
	file = CreateFile( packname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

	if ( file != INVALID_HANDLE_VALUE )
	{
		ret = WriteFile( file, (*buffer), (*bufferLen), &written, NULL );
		CloseHandle( file );
		mEndIO = true;
		GlobalFree( (*buffer) );
	}

	if ( file == INVALID_HANDLE_VALUE || ret == FALSE || mEndApp == true )
	{
		EventWrite("UnpackPathchList: Fail to save patch list file ");
		CloseHandle( file );
		mEndIO = true;
		GlobalFree( (*buffer) );
		return false;
	}

	/// 파일 압축 풀기
	if ( written == (*bufferLen) )
	{
		mEndIO = false;
		gzFile gz = gzopen( packname, "rb" );
		if ( gz != NULL )
		{
			char  buf[g16KBYTE];			// CHUNK 16KByte
			DWORD len = sizeof(buf);
			DWORD bytesRead    = 1;
			DWORD totalBytes   = 0;

			/// 파일 크기 얻기
			while ( bytesRead > 0 )
			{
				bytesRead = gzread( gz, buf, len );
				totalBytes += bytesRead;
				
				if( mEndApp == true)
				{
					gzclose( gz );
					mEndIO = true;
					DeleteFile( packname );
					return false;
				}
			}

			//메모리 할당
			(*bufferLen) = totalBytes;
			(*buffer) =  (char*)GlobalAlloc( GPTR, (*bufferLen)+1 );

			if ( (*buffer) == NULL || mEndApp == true )
			{
				EventWrite("UnpackPatchList:Failed to allocation patchlist buffer. ");
                GlobalFree( (*buffer) );
				gzclose( gz );
				mEndIO = true;
				DeleteFile( packname );
				return false;
			}

			gzrewind(gz);

			bytesRead = gzread( gz, (*buffer), (*bufferLen) );


			if( bytesRead != (*bufferLen) )
			{
				gzclose( gz );
				mEndIO = true;
				GlobalFree( (*buffer) );
				return false;
			}
			
// 			char reverse_buf[ g16KBYTE ];
// 			DWORD copied_size;
// 
// 			copied_size = 0;
// 
// 			/// 16k 씩 거꾸로 저장했던 파일을 원상태로 돌리기
// 			while ( copied_size < (*bufferLen) )
// 			{
// 				// 복사해야 할 메모리 크기
// 				DWORD remain_size = (*bufferLen) - copied_size;
// 
// 				// 복사할 메모리의 크기가 16k 이하인 경우
// 				if( remain_size < g16KBYTE )
// 				{
// 					for( DWORD cnt = 0; cnt < remain_size; ++cnt )
// 					{
// 						memcpy( &reverse_buf[cnt], &(*buffer)[ (*bufferLen) - cnt -1 ], 1 );
// 					}
// 					memcpy( &(*buffer)[copied_size] , reverse_buf, remain_size );
// 					copied_size += remain_size;
// 				}
// 				else
// 				{
// 					for( DWORD cnt = 0; cnt < g16KBYTE; ++cnt )
// 					{
// 						memcpy( &reverse_buf[cnt], &(*buffer)[ (g16KBYTE-cnt) + copied_size -1 ], 1 );
// 					}
// 					memcpy( &(*buffer)[ copied_size ], reverse_buf, g16KBYTE );
// 					copied_size += g16KBYTE;
// 				}
// 
// 			}

			gzclose( gz );
			mEndIO = true;
			DeleteFile( packname );
		}
		mEndIO = true;
		return true;
	}

	return false;
}



/*-- UpdateFile Method --*/
bool cPatcher::UpdateFile(Patchfile* patchfile, char* buffer, DWORD bufferLen)
{
	char  seps[] = "\\";
	HANDLE file;
	DWORD  written;
	bool   retcode = false;
	char  *buf = NULL;
	DWORD  bufLen = 0;

	DWORD start_time;

	start_time = GetTickCount();

	mEndIO = false;

	//testcode////////////////////////////////////////////////////////////////////////
	unsigned int ret;
	char tempDirectory[MAX_PATH];
	char tempname[MAX_PATH];
	
	/// 파일 다운로드 및 압축을 풀어서 버퍼에 저장.
	GetTempPath( sizeof(tempDirectory), tempDirectory );
	ret = GetTempFileName( tempDirectory, "Iris\0", 0, tempname );
	if( ret == 0 )
        ErrorPrint( "GetTempFileName Error" );

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

	file = CreateFile( tempname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( file != INVALID_HANDLE_VALUE )
	{
		if( WriteFile( file, buffer, bufferLen, &written, NULL ) == FALSE )
		{
			EventWrite( "UpdateFile: Fail to save [%s] file. GetLastError(%d) ", tempname, GetLastError() );
			CloseHandle( file );
			mEndIO = true;
			DeleteFile( tempname );
			return false;
		}

		CloseHandle( file );
		mEndIO = true;

		if( written == bufferLen )
		{
			mEndIO = false;
			gzFile gz = gzopen( tempname, "rb" );
			if ( gz != NULL )
			{
				char  readBuf[g16KBYTE];			// CHUNK 16KByte
				DWORD len = sizeof(readBuf);
				DWORD bytesRead    = 0;
				DWORD totalBytes   = 0;

				/// 파일 크기 얻기
				while ( true )
				{
					bytesRead = gzread( gz, readBuf, len );
					totalBytes += bytesRead;

					if( mEndApp == true )
					{
						gzclose( gz );
						mEndIO = true;
						DeleteFile( tempname );
						return false;
					}

					if( bytesRead < 1 )
						break;

				}

				//메모리 할당
				bufLen = totalBytes;
				buf =  (char*)GlobalAlloc( GPTR, bufLen+1 );

				if ( buf == NULL )
				{
					EventWrite( "UpdateFile: Fail to Memory Allocation. " );
					gzclose( gz );
					mEndIO = true;
					GlobalFree( buf );
					DeleteFile( tempname );
					return false;
				}

				gzrewind(gz);

				bytesRead = gzread( gz, buf, bufLen );
				if( bytesRead != bufLen )
				{
					EventWrite( "UpdateFile: Fail to Copy for buffer from [%s] Unpackfile. ",patchfile->fileName );
					gzclose( gz );
					mEndIO = true;
					GlobalFree( buf );
					DeleteFile( tempname );
					return false;
				}
				
				char reverse_buf[ g16KBYTE ];
				DWORD copied_size;
				char *pBuf;
				char *pCopyied_Pos;
				char *pReverse;

				pBuf         = buf;
				pCopyied_Pos = buf; // 현재 복사 위치
				copied_size  = 0;
				
				/// 16k 씩 거꾸로 저장했던 파일을 원상태로 돌리기
                while ( copied_size < bufLen )
				{
					// 복사해야 할 메모리 크기
					DWORD remain_size = bufLen - copied_size;
					
					pReverse = reverse_buf;
										
					// 복사할 메모리의 크기가 16k 이하인 경우
					if( remain_size < g16KBYTE )
					{
						pBuf     = pCopyied_Pos + remain_size - 1;

						for( DWORD cnt = 0; cnt < remain_size; ++cnt )
						{
							*pReverse = *pBuf;
							--pBuf;
							++pReverse;
							//memcpy( &reverse_buf[cnt], &buf[ bufLen - cnt -1 ], 1 );
						}
						memcpy( &buf[copied_size] , reverse_buf, remain_size );
						copied_size += remain_size;
					}
					else
					{
						pBuf = pCopyied_Pos + g16KBYTE - 1;

						for( DWORD cnt = 0; cnt < g16KBYTE; ++cnt )
						{
							*pReverse = *pBuf;
							--pBuf;
							++pReverse;
                            //memcpy( &reverse_buf[cnt], &buf[ (g16KBYTE-cnt) + copied_size -1 ], 1 );
						}
                        memcpy( &buf[ copied_size ], reverse_buf, g16KBYTE );
						copied_size += g16KBYTE;
						pCopyied_Pos += g16KBYTE;
					}

				}
			}
			gzclose( gz );
			mEndIO=true;
			
		}
		else 
		{
			/// 파일이 제대로 안써졌다면 파일 쓰기 에러.
			EventWrite( "UpdateFile: Fail to save temp update [%s] file. ",patchfile->fileName );
			DeleteFile( tempname );
			return false;
		}

		if( DeleteFile( tempname ) == FALSE )
		{
			EventWrite( "UpdateFile: Fail to delete temp [%s] file. ",patchfile->fileName );
		}
	}
	else
	{
		EventWrite( "UpdateFile: Fail to Create temp [%s] file. GetLastError:%d ",patchfile->fileName, GetLastError() );
		CloseHandle( file );
		mEndIO = true;
		DeleteFile( tempname );
		return false;
	}

	if( mEndApp == true )
		return false;

	printf("Time to Unpack and save buffer time is [%d]\n", (GetTickCount() - start_time));
	g_saveTime += (GetTickCount() - start_time);

	/// 기존 팩 파일 열기.
	char	packFileName[  1024 ];
	char	packFileNameWithPath[ 1024 ];
	char    fileNameDummy[ 1024 ];
	char   *folderName = NULL;
	char   *packFolderName   = NULL;
	bool    directSave = false;

	memset(  packFileName, 0, sizeof( packFileName ) );
	memset(  packFileNameWithPath, 0, sizeof( packFileNameWithPath ) );
	memset(  fileNameDummy, 0, sizeof( fileNameDummy ) );

	strncpy( fileNameDummy, patchfile->fileName, strlen( patchfile-> fileName ) );


	folderName = strtok( fileNameDummy, seps );
	strlwr( folderName );


	/// .pack 파일에 포함안되는 파일인지 체크. (ex : sound 폴더와 launcher 폴더, script등 ) /// script파일은 .pack파일을 통째로 받아 저장한다.  [1/9/2009 Jo_2]
	for(unsigned int index = 0; index < UNPACK_FOLDER_COUNT; ++index )
	{
		if( stricmp( folderName, gUnpackFolder[index] ) == 0 )
			directSave = true;
	}

	if( strchr( patchfile->fileName, '\\') == NULL || directSave == true )
	{
		char newName[ 1024 ];
		sprintf(newName, "%s\\%s\0", m_currentDirectory, patchfile->fileName );

		/// 압축이 필요없는 파일은 바로 파일로 저장하여 기존 파일을 삭제하고 추가한다.
		DWORD bytesWritten = 0;
		if( DeleteFile( newName ) == FALSE && GetLastError() != ERROR_FILE_NOT_FOUND)
		{
			EventWrite( "UpdateFile: Fail to Delete old [%s] file. Error Code:[%d] ", newName, GetLastError() );
		}

		mEndIO = false;
		file = CreateFile( newName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

		if ( WriteFile( file, buf, bufLen, &bytesWritten, NULL ) == false || file == INVALID_HANDLE_VALUE )
		{
			EventWrite( "UpdateFile: Fail to Save update [%s] file. Error Code:[%d] ", newName, GetLastError() );
			CloseHandle( file );
			mEndIO = true;
			return false;
		}

		/// 파일 날짜 설정
		SetFileTime( file, NULL, NULL, &patchfile->writeTime );

		DWORD    sizeHigh;
		DWORD    sizeLow;
		sizeLow = GetFileSize( file, &sizeHigh );

		CloseHandle( file );
		mEndIO = true;

		retcode = patchfile->sizeLow == sizeLow && patchfile->sizeHigh == sizeHigh;

		printf("Save normal type files ...time [%d]\n", (GetTickCount() - start_time));

		GlobalFree( buf );

		if( retcode == false )
		{	
			EventWrite( "Update:Fail! It's different save files[%s]. ", patchfile->fileName ); 
		}
		return retcode;
	}
	
	


	/// .pack파일이 있는지 체크
	packFolderName = strtok( NULL, seps );
	sprintf( packFileName, "%s.pack", packFolderName );
	strlwr(  packFileName );
	sprintf( packFileNameWithPath,"%s\\%s\\%s", m_currentDirectory, folderName, packFileName );

	start_time = GetTickCount();

	/// 해당 .pack 파일이 없다면 .pack 파일 생성하기.
	if ( _access( packFileNameWithPath, 0 ) != 0 )
	{
		cFilePack pack;
		
		if( mEndApp == true )
			return false;

		mEndIO = false;
		if( pack.Open( packFileNameWithPath, cFilePack::CREATE) == true )
		{
			/// .pack 열기
			pack.SetPassword( PACK_PASSWORD );

			cFileInPackInfo fipi;
			fipi.SetPathName( patchfile->fileName );

			time_t time;
			FILETIME write;

			write = patchfile->writeTime;

			const __int64 SecsTo100ns = 10000000;
			const __int64 SecsBetweenEpochs = 11644473600;
			__int64 converted_time;
			converted_time = ((__int64)write.dwHighDateTime << 32) + write.dwLowDateTime;
			converted_time -= (SecsBetweenEpochs * SecsTo100ns);
			converted_time /= SecsTo100ns;
			time = (time_t)converted_time;
			fipi.SetWriteTime( time );

//			// 시간 확인 코드
//			time_t getTime;
//			FILETIME newWrite;
//
//  		getTime = fipi.GetUnixWriteTime();
//  			
//  		LONGLONG ll;
//  		ll = Int32x32To64(getTime, 10000000) + 116444736000000000;
//			newWrite.dwLowDateTime = (DWORD)ll;
//  		newWrite.dwHighDateTime = ll >> 32;
// 			
// 			if( CompareFileTime( &newWrite, &write ) != 0 )
// 				printf("");


			if( pack.OpenNewFile( fipi ) == false )
			{
				EventWrite( "UpdateFile: Fail to OpenNewFile [%s] file for [%s]Pack file. ", patchfile->fileName, packFileNameWithPath );
				pack.CloseFile();
				mEndIO = true;
				GlobalFree( buf );
				return false;
			}

			if( pack.WriteNewFile( buf, bufLen ) == false )
			{
				EventWrite( "UpdateFile: Fail to WriteNewFile [%s] file for [%s]Pack file. ", patchfile->fileName, packFileNameWithPath );
				pack.CloseFile();
				mEndIO = true;
				GlobalFree( buf );
				return false;
			}

			pack.CloseFile();
			mEndIO = true;

			printf("End Create [%s] file...during time is [%d]\n", packFileName, (GetTickCount() - start_time) );
			g_createTime += (GetTickCount() - start_time);

			GlobalFree( buf );
			return true;
		}

		EventWrite("UpdateFile: Fail to create [%s] pack file. ",packFileNameWithPath );

		pack.CloseFile();
		mEndIO = true;
		GlobalFree( buf );
		return false;

	}
	else
	{
		/// .pack 파일이 있다면 파일을 열기
		cFilePack pack;
		DWORD deleteIndex = 0;
		mEndIO = false;
		if( pack.Open( packFileNameWithPath, cFilePack::OPEN) == true && mEndApp == false )
		{
			pack.SetPassword( PACK_PASSWORD );

  			deleteIndex = pack.FindFile( patchfile->fileName ); 
			
  			/// 기존에 .pack 파일 안에 있는 파일을 업데이트 시킨것이라면 기존 파일을 삭제
  			if( deleteIndex != -1 )
  				pack.DeleteFile( deleteIndex );
			
			/// .pack 파일에 추가 루틴
			cFileInPackInfo fipi;
			fipi.SetPathName( patchfile->fileName );

			time_t time;
			FILETIME write;

			write = patchfile->writeTime;

			const __int64 SecsTo100ns = 10000000;
			const __int64 SecsBetweenEpochs = 11644473600;
			__int64 converted_time;
			converted_time = ((__int64)write.dwHighDateTime << 32) + write.dwLowDateTime;
			converted_time -= (SecsBetweenEpochs * SecsTo100ns);
			converted_time /= SecsTo100ns;
			time = (time_t)converted_time;
			
			fipi.SetWriteTime( time );

			if( pack.OpenNewFile( fipi ) == false )
			{	
				EventWrite("UpdateFile: Fail to WriteNewFile [%s] file for [%s]Pack file. ", patchfile->fileName, packFileNameWithPath );
				pack.CloseFile();
				mEndIO = true;
				GlobalFree( buf );

				return false;
			}
	
			if( pack.WriteNewFile( buf, bufLen ) == false )
			{
				EventWrite( "UpdateFile: Fail to WriteNewFile [%s] file for [%s]Pack file ", patchfile->fileName, packFileNameWithPath );
				pack.CloseFile();
				mEndIO = true;
				GlobalFree( buf );
				return false;
			}

			pack.CloseFile();
			mEndIO = true;
			GlobalFree( buf );
						
			printf("End Add [%s] file...during time is [%d]\n", packFileName, ( GetTickCount() - start_time) );
			g_addTime += ( GetTickCount() - start_time);
			return true;
		}

		EventWrite("UpdateFile:Fail to open [%s]pack file. ",packFileNameWithPath );

		/// 잘못된 팩 파일 지워버리기.
		DeleteFile( packFileNameWithPath );

		pack.CloseFile();
		mEndIO = true;
		GlobalFree( buf );

		return false;
	}
	return retcode;
}


void cPatcher::ErrorPrint(char *msg)
{
	LPVOID lpMsgBuf;
	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER|
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPSTR)&lpMsgBuf, 0, NULL);
	MessageBox(mWnd, (LPCSTR)lpMsgBuf, msg, MB_ICONERROR);
	LocalFree(lpMsgBuf);
}


/*-- Search Method
*/
int cPatcher::Search(char* root, char* sub, int& files, int& directories, bool opcode)
{
	HANDLE          search = NULL;
	char            where[ 1024 ];

	char            filename[ 1024 ];
	WIN32_FIND_DATA fileData;

	// 하위 디렉토리 적용여부
	if ( sub )
		sprintf( where, "%s\\%s", root, sub );
	else
		sprintf( where, "%s", root );

	sprintf( filename, "%s\\*.*", where );

	search = FindFirstFile( filename, &fileData );
	if ( search != INVALID_HANDLE_VALUE )
	{
		do {
				if( mEndApp == true )
					return -1;

				if ( fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
				{
					if ( (strcmp( fileData.cFileName, "."  ) != 0) && (strcmp( fileData.cFileName, ".." ) != 0) )
					{
						
						/// 클라이언트 리소스 파일과 상관 없는 폴더 제외.
						/// 상위 폴더가 Screenshot 이거나 Option 일 경우 원하지 않는 결과를 초래하므로 개선해야함. 거꾸로 해당 이름만큼만 비교하자.(개선사항)
// 						for(unsigned int index = 0; index < SKIP_FOLDER_COUNT; ++index )
// 						{
// 							if( stricmp( sub, gSkipFolder[index] ) == 0 )
// 								continue;
// 						}

						char directory[ 1024 ];

						if ( sub )
							sprintf( directory, "%s\\%s", sub, fileData.cFileName );
						else
							sprintf( directory, "%s", fileData.cFileName );

						Search( root, directory, files, directories, opcode );	// 재귀호출.
						
						directories += 1; // 폴더 수 누적.
					}
				}
				else if ( fileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE )
				{
					char fullpath[ 1024 ];

					if ( sub )
						sprintf( fullpath, "%s\\%s\\%s", root, sub, fileData.cFileName );
					else
						sprintf( fullpath, "%s\\%s", root, fileData.cFileName );

					if ( opcode )
					{
						Patchfile file;
						bool      IsScriptSubPackfile = false;

						memset( file.fileName, 0 , sizeof( file.fileName ) );

						if ( sub )
						{
							sprintf( file.fileName, "%s\\%s", sub, fileData.cFileName );

							if( stricmp( sub ,SCRIPT_DIRECTORY ) == 0 )
								IsScriptSubPackfile = true;
						}
						else
							sprintf( file.fileName, "%s", fileData.cFileName );

						/// 파일이 .pack 파일이라면 파일을 풀어서 파일 안에 있는 데이터 정보를 담아야 함.
						/// Script 폴더안에 있는 pack 파일들은 하나의 일반 파일로 봐야함.
						if( strstr( fileData.cFileName, ".pack" ) != NULL && IsScriptSubPackfile == false ) 
						{
							cFilePack pack;
							if( pack.Open( fullpath, cFilePack::OPEN_READONLY ) == true )
							{
								for( unsigned int index = 0, index_limit = pack.GetNumFiles(); index < index_limit && mEndApp == false; ++index )
								{
									/// .pack 열기
									cFileInPackInfo fipi;
							
									if( pack.GetFileInfo( &fipi, index) == false )
										continue;
										//return false;

									if( fipi.IsDirectory() )
									{
										continue;
										//return false;
									}
									else
									{
										DWORD    sizeHigh = 0;
										DWORD    sizeLow = 0;
										FILETIME write;
										time_t   time;
										LONGLONG ll;

										/// 명확성 검사. fipi.pathname 과 filename을 비교하여 현재 맞는 경로에 있는 pack 파일을 열었는지 검사.
										/// 게임 설치 폴더 안에 pack 파일이 다른 곳으로 옮겨 졌거나, 다른 이름으로 저장되었다면 현재 pack 파일 무시.
										/// 그리고 이름은 다르지만 안의 내용물이 동일한 파일이 있다면 key 값으로 들어가는 fipi.pathname 이 겹치므로 현재 pack 파일 무시해야 함.
										if( index == 0 && strnicmp( file.fileName, fipi.GetPathName().Cstr(), strlen(file.fileName) - 5 ) != 0 )
											break;

										sizeLow = fipi.GetSize();
										time = fipi.GetUnixWriteTime();


										ll = Int32x32To64(time, 10000000) + 116444736000000000;
										write.dwLowDateTime = (DWORD)ll;
										write.dwHighDateTime = ll >> 32;

										//strncpy( file.fileName, fipi.GetPathName().Cstr() , strlen( fipi.GetPathName().Cstr() ) );
										sprintf( file.fileName, "%s", fipi.GetPathName().Cstr() );
										file.sizeHigh  = sizeHigh;
										file.sizeLow   = sizeLow;
										file.writeTime = write;
			
										FileTimeToDOS( &file.writeTime );
												
										if ( sizeLow != file.sizeLow || sizeHigh != file.sizeHigh )
											continue;

										if ( CompareFileTime( &write, &file.writeTime ) != 0 )
										{
											printf("Fail to conversion filetime to unixtime \n");
											continue;
										}
										
										if ( m_compressedFileList.Add_Node( &file ) == false )
											printf("Fail to add [%s]client list. It maybe Already registered key \n",file.fileName );


									}
								}
								files += pack.GetNumFiles(); // 파일 수 누적.
							}
							else
							{
								/// .pack 파일이 존재함에도 여는게 실패하였을 경우 해당 pack 파일 문제로 보고, pack 파일 삭제!
								if( _access( fullpath, 0) == 0 )
									 DeleteFile( fullpath );
							}

							pack.Close();

							if( mEndApp == true )
								return -1;

							continue;
						}
					
						file.sizeHigh  = fileData.nFileSizeHigh;
						file.sizeLow   = fileData.nFileSizeLow;
						file.writeTime = fileData.ftLastWriteTime;

						FileTimeToDOS( &file.writeTime );

						if ( m_UnCompressedFileList.Add_Node( &file ) == false )
							printf("Fail to add [%s]client list. It maybe Already registered key \n",file.fileName );
					}

					files += 1; // 파일 수 누적.
				}
		} while ( FindNextFile( search, &fileData ) );

		FindClose( search );
		
		if(mEndApp == TRUE)
			return -1;

		return 0;
	}
	return -1; // 검색 실패.
}


/*-- VerifyFile Method --*/
bool cPatcher::VerifyFile(Patchfile* patchfile)
{

	/// 파일 검색 루틴
	Patchfile *findFile;
	
	if( mEndApp == true )
		return false;
	
	/// patcher 는 launcher가 이미 업데이트 시켰으므로 통과.
	if( stricmp( patchfile->fileName, PATCHER_FILENAME ) == 0 )
		return true;

	/// 압축된 파일에서 먼저 데이터를 검색한다.

	findFile = m_compressedFileList.Search_Node( patchfile->fileName );

	if( findFile == NULL )
	{
		///압축된 파일에서 못찾았다면 압축이 안된 일반 파일 리스트에서 찾기
		findFile = m_UnCompressedFileList.Search_Node( patchfile->fileName );
		
		if( findFile == NULL )
            return false;
	}

	if( ( findFile->sizeHigh == patchfile->sizeHigh ) &&
		( findFile->sizeLow  == patchfile->sizeLow  ) &&
		( findFile->writeTime.dwHighDateTime == patchfile->writeTime.dwHighDateTime ) &&
		( findFile->writeTime.dwLowDateTime  == patchfile->writeTime.dwLowDateTime ) )
		return true;

	return false;
}

/*-- EventWrite Method
*/
void cPatcher::EventWrite(LPCTSTR format, ...)
{
	LPVOID  msgBuf = NULL;
	DWORD   bufferLength;
	va_list args;

	if( mEndApp == true )
		return;

	va_start( args, format );

	bufferLength = _vscprintf( format, args ) +1;
	msgBuf       = malloc( bufferLength );
	vsprintf( (char*)msgBuf, format, args );

	va_end( args );

	time_t t;
	tm *pt;
	char time_array[ 1024 ];

	time(&t);
	pt=localtime(&t);

	sprintf( time_array, "%d/%d/%d %d:%d:%d \0",	pt->tm_year+1900, pt->tm_mon+1, pt->tm_mday, pt->tm_hour,pt->tm_min,pt->tm_sec );

	MessageBox(mWnd, (LPCTSTR)msgBuf, "Error", MB_ICONERROR);

// 	if ( msgBuf != NULL )
// 	{
// 		char filename[1024];
// 		sprintf( filename, "%s\\%s.log", m_currentDirectory, PATCHER );
// 
// 		FILE* stream = fopen( filename, "at" );
// 		if ( stream != NULL )
// 		{
// 			fputs( time_array   , stream );
// 			fputs( (char*)msgBuf, stream );
// 			fputs( "\n\0"       , stream);
// 			fclose( stream );
// 			stream = NULL;
// 		}
// 
// 		free( msgBuf );
// 	}
}

