/*-- Include
*/
#include "launcher.h"
#include <stdio.h>

/*-- 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);

#define WM_UPDATE_MSG	WM_USER+0x10
#define TRAY_NOTIFY     WM_USER+0x11

enum UPDATE_MSG_TYPE
{
	_UMT_UPDATE_OPEN_,
	_UMT_UPDATE_LIST_,
	_UMT_UPDATE_FILE_,
	_UMT_UPDATE_COMPLETE_,
};

/*-- Global data
*/
HINSTANCE         g_instance         = NULL;
Launcher*         g_launcher         = NULL;

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

/*-- WinMain Prototype
*/
int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
{
	// Initialize COM Library - INTERNET API(wininet.h/wininet.lib)를 사용하기 위해 선언.
// 	if ( CoInitialize( NULL ) != S_OK )
// 	{
// 		MessageBox( NULL, "CoInitialize Failed!\r\n", "Launcher", MB_OK );
// 		return 0L;
// 	}

	InitCommonControls( );

	Launcher* launcher = new Launcher( );
	HANDLE    handle   = CreateMutex( NULL, FALSE, launcher->GetWindowName( ) );
	int       result   = GetLastError( );

	g_instance = instance;
	if ( handle != NULL )
	{
		if ( result != ERROR_ALREADY_EXISTS )
			result = launcher->Run( instance, prevInstance, cmdLine, cmdShow );
		CloseHandle( handle );
	}

	SafeDelete( launcher );

	// Finished with COM Library
//	CoUninitialize( );
	return result;
}

/*-- DlgProc Prototype
*/
LRESULT CALLBACK DlgProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return g_launcher->MsgProc( wnd, msg, wParam, lParam );
}

/*-- WorkerThreadStartingPoint Prototype
*/
DWORD WINAPI WorkerThreadStartingPoint(void* ptr)
{
	return g_launcher->WorkerThread( );
}

/*-- Launcher Constructor
*/
Launcher::Launcher(void)
{
	g_launcher     = this;
	m_wnd          = NULL;
	m_icon         = NULL;
	m_windowName   = TEXT( "Launcher - IRIS ON LINE" );
	m_endApp       = false;
	m_endIO		   = true;
	m_runApp       = false;

	m_libClassDesc = NULL;

	m_packlist.pack    = NULL;
	m_packlist.offset  = 0;
	m_packlist.length  = 0;

	m_patchlist.file   = NULL;
	m_patchlist.offset = 0;
	m_patchlist.length = 0;

	m_workerThread = NULL;
}

/*-- ~LiveUpdate Destructor
*/
Launcher::~Launcher(void)
{
	if ( m_packlist.pack != NULL )
	{
		GlobalFree( m_packlist.pack );
		m_packlist.pack = NULL;
		m_packlist.offset = 0;
		m_packlist.length = 0;
	}
	if ( m_patchlist.file != NULL )
	{
		GlobalFree( m_patchlist.file );
		m_patchlist.file   = NULL;
		m_patchlist.offset = 0;
		m_patchlist.length = 0;
	}

	g_launcher = NULL;
	
	SafeDelete( m_libClassDesc );
	m_libClassDesc = NULL;

}


/*-- FileTimeToUTC Method
*/
void Launcher::FileTimeToUTC(FILETIME* filetime)
{
	SYSTEMTIME utc;
	FileTimeToSystemTime( filetime, &utc );
	SystemTimeToFileTime( &utc, filetime );
}

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

/*-- Run Method
*/
int Launcher::Run(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
{
	// 현재 폴더 저장.
	GetCurrentDirectory( sizeof(m_currentDirectory), m_currentDirectory );

	// 응용 프로그램, 다이얼로그 생성
	m_wnd = CreateDialog( instance, MAKEINTRESOURCE(IDD_LAUNCHER), NULL, (DLGPROC)DlgProc );
	if ( m_wnd == NULL )
		return GetLastError( );

	// 윈도우 이름 입력
	SetWindowText( m_wnd, GetWindowName( ) );

	// 응용 프로그램, 다이얼로그 표시
	ShowWindow( m_wnd, cmdShow );
	//ShowWindow( m_wnd, SW_HIDE );

	// 업데이트 시작.
	DWORD threadId;
	m_workerThread = CreateThread( NULL, 0, WorkerThreadStartingPoint, (LPVOID)this, 0, &threadId );
	if ( m_workerThread == NULL )
	{
		DWORD lastError = GetLastError( );
		char  buffer[MAX_PATH];

		sprintf( buffer, "Error(=0x%08x)", lastError );
		MessageBox( m_wnd, buffer, GetWindowName( ), MB_OK | MB_ICONERROR );
		PostMessage( m_wnd, WM_CLOSE, 0, 0 );
	}

	// 메시지 루프 시작
	MSG  msg;
	BOOL result;

	while ( (result = GetMessage( &msg, NULL, 0, 0 )) )
	{
		if ( result == -1 )
		{
			// handle the error and possibly exit
			return GetLastError( );
		}
		else if ( !IsWindow( m_wnd ) || !IsDialogMessage( m_wnd, &msg ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
	}

	// Patcher 실행
	if ( m_runApp == true )
	{
		SetCurrentDirectory( m_currentDirectory );

		char execute[ MAX_PATH ];

		if ( GetString( IDS_UPDATE_EXECUTE, execute, sizeof(execute) ) > 0 )
		{
			WinExec( execute, SW_SHOWNORMAL );
		}

	}
	return (int)msg.wParam;
}

/*-- MsgProc Method
*/
LRESULT Launcher::MsgProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch ( msg )
	{
	case WM_DESTROY:       return Destroy    ( wnd, wParam, lParam );
	case WM_INITDIALOG:    return InitDialog ( wnd, wParam, lParam );
	case WM_COMMAND:       return Command    ( wnd, wParam, lParam );
	case WM_UPDATE_MSG:    return UpdateMsg  ( wnd, wParam, lParam );
	case TRAY_NOTIFY: 	   
		if( lParam == WM_LBUTTONDBLCLK ) 
			return ::ShowWindow( wnd, IsWindowVisible( wnd ) ? SW_HIDE : SW_SHOW );
	}
	return 0L;
}

/*-- SetStatus Method
*/
BOOL Launcher::SetStatus(HWND wnd, char* msg)
{
	HWND dlgItem = GetDlgItem( wnd, IDC_STATUS );
	BOOL retcode = SetWindowText( dlgItem, msg );
	UpdateWindow( dlgItem );
	return retcode;
}

/*-- Destroy Method
*/
LRESULT Launcher::Destroy(HWND wnd, WPARAM wParam, LPARAM lParam)
{
	// 안전종료.
	m_endApp = true;

	// file I/O 가 끝날때까지 대기
	while( m_endIO != true )
	{
		Sleep(0);
		// wait for IO...
	}

	// m_updateThread 핸들를 해제.
	if ( m_workerThread != NULL )
	{
		// UpdateThread가 종료될때까지 대기.
		if ( WaitForSingleObject( m_workerThread, 5000 ) != WAIT_OBJECT_0 )
		{
			DWORD exitCode;
			GetExitCodeThread( m_workerThread, &exitCode );
			if ( exitCode == STILL_ACTIVE )
			{
				TerminateThread( m_workerThread, 0 );
			}
		}
		CloseHandle( m_workerThread );
		m_workerThread = NULL;
	}

	// ICON 해제
	if ( m_icon != NULL )
	{
		DestroyIcon( m_icon );
		m_icon = NULL;
	}
	
/// 트레이 아이콘 해제
// 	NOTIFYICONDATA nid;
// 	nid.cbSize = sizeof( NOTIFYICONDATA );
// 	nid.hWnd   = wnd;
// 	nid.uID    = 0;
// 	Shell_NotifyIcon( NIM_DELETE , &nid );


	// 응용 프로그램을 종료 한다.
	PostQuitMessage( 0 );
	return 0L;
}

/*-- InitDialog Method
*/
LRESULT Launcher::InitDialog(HWND wnd, WPARAM wParam, LPARAM lParam)
{
	// ICON 로드
	m_icon = LoadIcon( g_instance, MAKEINTRESOURCE( IDI_ICON ) );
	SendMessage( wnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)m_icon );

	// Progress Bar - 범위 설정(0 ~ 100%).
	SendMessage( GetDlgItem( wnd, IDC_PROGRESS ), PBM_SETRANGE, 0, MAKELPARAM(0,100) ); 

/// 트레이 아이콘 구현
// 	NOTIFYICONDATA nid;
// 	nid.cbSize = sizeof(NOTIFYICONDATA);
// 	nid.hWnd = wnd;
// 	nid.uID = 0;
// 	nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
// 	nid.uCallbackMessage = TRAY_NOTIFY;
// 	nid.hIcon = LoadIcon( g_instance, MAKEINTRESOURCE(IDI_ICON) );
// 	lstrcpy( nid.szTip, GetWindowName() );
// 	Shell_NotifyIcon( NIM_ADD, &nid);

	// Dll 로드
	SetStatus( wnd, "Ready" );
	return 1L;
}

/*-- Command Method
*/
LRESULT Launcher::Command(HWND wnd, WPARAM wParam, LPARAM lParam)
{
	switch ( LOWORD(wParam) )
	{
	case IDOK:
		//DestroyWindow( wnd );
		break;
	case IDCANCEL:
		DestroyWindow( wnd );
		break;
	}
	return 0L;
}

/*-- UpdateMsg Method
*/
LRESULT Launcher::UpdateMsg(HWND wnd, WPARAM wParam, LPARAM lParam)
{
	switch ( wParam )
	{
	case _UMT_UPDATE_OPEN_:
		MessageBox( wnd, "ERROR URL", GetWindowName( ), MB_OK );
		SetStatus( wnd, "Error URL" );
		break;
	case _UMT_UPDATE_LIST_:
		MessageBox( wnd, "ERROR DOWNLOAD", GetWindowName( ), MB_OK );
		SetStatus( wnd, "Error Download" );
		break;
	case _UMT_UPDATE_FILE_:
		MessageBox( wnd, "ERROR UPDATE FILE", GetWindowName( ), MB_OK );
		SetStatus( wnd, "Error Update file" );
		break;
	case _UMT_UPDATE_COMPLETE_:
		break;
	default:
		break;
	}
	PostMessage( m_wnd, WM_CLOSE, 0, 0 );
	return 0L;
}

/*-- LoaderProgress Method
*/
DWORD Launcher::LoaderProgress(DWORD totalFileSize, DWORD totalBytesTransferred, void* data)
{
	if ( m_wnd != NULL )
	{
		int percent = (int)((float)totalBytesTransferred / (float)totalFileSize * 100.f);
		SendMessage( GetDlgItem( m_wnd, IDC_PROGRESS ), PBM_SETPOS, percent, 0 );
	}
	return (m_endApp != true) ? LPR_CONTINUE : LPR_STOP;
}

/*-- LoaderProgressRoutine Method
*/
DWORD Launcher::LoaderProgressRoutine(DWORD totalFileSize, DWORD totalBytesTransferred, void* data)
{
	Launcher* launcher = (Launcher*)data;
	return launcher->LoaderProgress( totalFileSize, totalBytesTransferred, data );
}

/*-- MakeList Method
*/
bool Launcher::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 );

	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 );

	// Body
	Packfile* packfile = m_packlist.pack;
	for ( ULONG i = 0; i < m_packlist.length; 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; 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 );
	}
	return true;
}

/*-- VerifyFile Method
*/
bool Launcher::VerifyFile(Patchfile* patchfile)
{
	char     filename[ 1024 ];
	HANDLE   file;

	/// 프로그램이 종료되었다면 빠른 종료를 위해 임시로 true 리턴.
	if( m_endApp == true )
		return true;

	sprintf( filename, "%s\\%s", m_currentDirectory, patchfile->fileName );
	m_endIO = false;
	file = CreateFile( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL );

	if ( file != INVALID_HANDLE_VALUE && m_endApp == false )
	{
		DWORD    sizeHigh;
		DWORD    sizeLow;
		FILETIME write;
		bool     retcode = true;

		sizeLow = GetFileSize( file, &sizeHigh );
		GetFileTime( file, NULL, NULL, &write );

		if ( sizeLow != patchfile->sizeLow || sizeHigh != patchfile->sizeHigh )
			retcode = false;

		FileTimeToDOS( &write );
		if ( CompareFileTime( &write, &patchfile->writeTime ) != 0 )
			retcode = false;

		CloseHandle( file );
		m_endIO = true;
		return retcode;
	}
	else
	{
		CloseHandle( file );
		m_endIO = true;
		return false;
	}

	return false;
}

/*-- UpdateFile Method
*/
bool Launcher::UpdateFile(Patchfile* patchfile, char* buffer, DWORD bufferLen)
{
	// 폴더 생성.
	char drive    [ _MAX_DRIVE ] = {0,};
	char directory[ _MAX_DIR   ] = {0,};
	char makeDir  [ 1024       ] = {0,};
	char filename [ 1024       ] = {0,};

	char  seps[] = "/\\";
	char* token;

	sprintf( filename, "%s\\%s", m_currentDirectory, patchfile->fileName );
	_splitpath( filename, drive, directory, NULL, NULL );
	memset( makeDir, 0, sizeof(makeDir) );

	strcat( makeDir, drive );
	strcat( makeDir, "/" );

	token = strtok( directory, seps );
	while ( token != NULL )
	{
		strcat( makeDir, token );
		strcat( makeDir, "/" );
		
		if ( GetFileAttributes( makeDir ) == 0xFFFFFFFF )
		{
			if ( CreateDirectory( makeDir, NULL ) == FALSE )
				return false;
		}
		token = strtok( NULL, seps );
	}

	char   tempname[ 1024 ] = {0,};
	HANDLE file;
	DWORD  written;
	bool   retcode = false;

	sprintf( tempname, "%s.tmp", filename );
	m_endIO = false;
	file = CreateFile( tempname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( file != INVALID_HANDLE_VALUE )
	{
		WriteFile( file, buffer, bufferLen, &written, NULL );
		CloseHandle( file );
		m_endIO = true;
		if ( written == bufferLen )
		{
			m_endIO = false;
			gzFile gz = gzopen( tempname, "rb" );
			if ( gz != NULL )
			{
				m_endIO = false;
				file = CreateFile( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
				if ( file != INVALID_HANDLE_VALUE )
				{
					char  buf[16384];			// CHUNK 16KByte
					DWORD len = sizeof(buf);

					DWORD bytesRead;
					DWORD bytesWritten;
					while ( true )
					{
						
						if( m_endApp == true )
						{
							gzclose( gz );
							CloseHandle( file );
							m_endIO = true;
							DeleteFile( tempname );
							return false;
						}

						bytesRead = gzread( gz, buf, len );
						if ( bytesRead > 0 )
						{
							char  reverse_buf[16384];
							char* pbuf = buf + bytesRead - 1;
							char* prev = reverse_buf;
							for( DWORD cnt = 0; cnt < bytesRead; ++cnt )
							{
								*prev = *pbuf;
								pbuf--;
								prev++;
							}

							WriteFile( file, reverse_buf, bytesRead, &bytesWritten, NULL );
						}
						else
							break;
					}
					SetFileTime( file, NULL, NULL, &patchfile->writeTime );

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

					CloseHandle( file );
					m_endIO = true;

					retcode = patchfile->sizeLow == sizeLow && patchfile->sizeHigh == sizeHigh;
				}
				gzclose( gz );
				m_endIO = true;
			}
		}

		DeleteFile( tempname );
	}

	return retcode;
}

/*-- WorkerThread Method
*/
DWORD Launcher::WorkerThread( )
{
	m_libClassDesc = new Loader;

	if( m_libClassDesc == NULL )
	{
		PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_OPEN_, (LPARAM)0 );
		return _UMT_UPDATE_OPEN_;
	}

	char hostaddr[ 1024 ] = {0,};
	char hosturl [ 1024 ] = {0,};
	char hostlist[ 1024 ] = {0,};
	char port    [ 1024 ] = {0,};

	GetString( IDS_UPDATE_HOST, hostaddr, sizeof(hostaddr) );
	GetString( IDS_UPDATE_URL,  hosturl,  sizeof(hosturl)  );
	GetString( IDS_UPDATE_LIST, hostlist, sizeof(hostlist) );

#ifdef _SERVER_MODE_
	GetString( IDS_UPDATE_HOST_INSIDE, hostaddr, sizeof(hostaddr) );
	GetString( IDS_UPDATE_GM_PORT, port, sizeof(port) );
	GetString( IDS_UPDATE_SERVER_URL,  hosturl,  sizeof(hosturl)  );
#endif

#ifdef _GM_MODE_
	GetString( IDS_UPDATE_HOST_INSIDE, hostaddr, sizeof(hostaddr) );
	GetString( IDS_UPDATE_GM_PORT, port, sizeof(port) );
	GetString( IDS_UPDATE_GMTOOL_URL,  hosturl,  sizeof(hosturl)  );
#endif

	Sleep( 1000 );

	SetStatus( m_wnd, "Start" );

#if defined(_GM_MODE_) || defined(_SERVER_MODE_) 
	if (  m_libClassDesc->Open( hostaddr , atoi(port) ) != 0 )
	{
		PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_OPEN_, (LPARAM)0 );
		return _UMT_UPDATE_OPEN_;
	}
#else 
	if (  m_libClassDesc->Open( hostaddr ) != 0 )
	{
		PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_OPEN_, (LPARAM)0 );
		return _UMT_UPDATE_OPEN_;
	}
#endif

	char  url[ 1024 ] = {0,};
	char* buffer    = NULL;
	DWORD bufferLen = 0;

	DWORD exitCode;
	DWORD retValue;

	// 패치리스트 목록 생성.

	sprintf( url, "%s/%s", hosturl, hostlist );
	exitCode = LPR_CONTINUE;
	retValue = m_libClassDesc->Get( url, &LoaderProgressRoutine, &exitCode, this, &buffer, &bufferLen );
    
	/// 중간에 접속이 재설정 되었다면
	if( retValue == ERROR_INTERNET_CONNECTION_RESET )
	{
		int retry = 0;   

		do
		{
			/// 재 접속
			if( m_libClassDesc->Open( hostaddr ) ) 
			{
				return _UMT_UPDATE_LIST_;
			}

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

		} while ( retry < 3 && retValue != 0 );
	}

	if ( buffer != NULL && bufferLen > 0 && retValue == 0 )
	{
		if( UnpackPatchList( &buffer, &bufferLen ) == false ) 
			return _UMT_UPDATE_LIST_;

		bool ismake = MakeList( buffer );
		GlobalFree( buffer );
		if ( ismake != true )
		{
			PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_LIST_, (LPARAM)0 );
			return _UMT_UPDATE_LIST_;
		}
	}
	else
	{
		PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_LIST_, (LPARAM)0 );
		return _UMT_UPDATE_LIST_;
	}

	// 패치리스트 목록 검사.
	Patchfile* files = m_patchlist.file;
	for ( ULONG i = 0; i < m_patchlist.length && m_endApp == false ; i++, files++ )
	{
		if ( VerifyFile( files ) == false )
		{
			sprintf( url, "%s/%s.z", hosturl, files->fileName );
			exitCode = LPR_CONTINUE;
			retValue = m_libClassDesc->Get( url, &LoaderProgressRoutine, &exitCode, this, &buffer, &bufferLen );
			
			/// 중간에 접속이 재설정 되었다면
			if( retValue == ERROR_INTERNET_CONNECTION_RESET )
			{
				int retry = 0;   

				do
				{
					/// 재 접속
					if( m_libClassDesc->Open( hostaddr ) ) 
					{
						return _UMT_UPDATE_LIST_;
					}

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

				} while ( retry < 3 && retValue != 0 );
			}

			if ( buffer != NULL && bufferLen > 0 && m_endApp == false )
			{
				if ( UpdateFile( files, buffer, bufferLen ) == false )
				{
					PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_FILE_, (LPARAM)0 );
					return _UMT_UPDATE_FILE_;
				}
			}
			else
			{
				PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_FILE_, (LPARAM)0 );
				return _UMT_UPDATE_FILE_;
			}

			GlobalFree( buffer );
		}
	}

	m_libClassDesc->Close( );
	SetStatus( m_wnd, "End" );

	Sleep( 500 );

	// 업데이트 완료.
	if( m_endApp == false )
	{

#if defined(_SERVER_MODE_) || defined(_GM_MODE_) 
		MessageBox( m_wnd, "Update Complete", "End", MB_OK );
#else
		m_runApp = true;
#endif
		PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_COMPLETE_, (LPARAM)0 );
	}

	return 0L;
}

bool Launcher::UnpackPatchList( char** buffer, DWORD *bufferLen )
{
	char   hostlist[1024] = {0,};
	char   packname[1024] = {0,};
	HANDLE file;
	DWORD  written;
	BOOL   ret      = FALSE;
	DWORD  lowSize  = 0;
	DWORD  highSize = 0;

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

	/// 다운 받은 patchlist.txt.z파일 저장.
	m_endIO = false;
	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 );
		m_endIO = true;
		GlobalFree( (*buffer) );
	}
	else if ( ret == 0 || m_endApp == true )
	{
		PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_LIST_, (LPARAM)0 );
		CloseHandle( file );
		m_endIO = true;
		GlobalFree( (*buffer) );
		return false;
	}

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

			/// 파일 크기 얻기
			while ( bytesRead > 0 )
			{
				bytesRead = gzread( gz, buf, len );
				totalBytes += bytesRead;
			}

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

			if ( (*buffer) == NULL || m_endApp == true )
			{
				PostMessage( m_wnd, WM_UPDATE_MSG, (WPARAM)_UMT_UPDATE_LIST_, (LPARAM)0 );
				gzclose( gz );
				m_endIO = true;
				GlobalFree( (*buffer) );
				return false;
			}

			gzrewind(gz);

			bytesRead = gzread( gz, (*buffer), (*bufferLen) );
			if( bytesRead != (*bufferLen) )
			{
				//ErrorPrint("Fail to Copy for buffer from Unpackfile" );
				gzclose( gz );
				m_endIO = true;
				GlobalFree( (*buffer) );
				return false;
			}

			gzclose( gz );
			m_endIO = true;
			DeleteFile( packname );
		}

		return true;
	}

	return false;
}