// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // branch_patcherDlg.cpp : implementation file // #include "stdafx.h" #include "branch_patcher.h" #include "branch_patcherDlg.h" #include "shlobj.h" #include "direct.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CBranch_patcherDlg dialog extern CBranch_patcherApp theApp; const CString TEMP_DIFF_FILE = "C:\\tempFile.diff"; const CString DIFF_ERRORS = "C:\\diffLog.txt"; const CString PATCH_RESULT = "C:\\patchResult.txt"; const CString PATCH_ERRORS = "C:\\patchErrors.txt"; CBranch_patcherDlg::CBranch_patcherDlg(CWnd* pParent /*=NULL*/) : CDialog(CBranch_patcherDlg::IDD, pParent) { //{{AFX_DATA_INIT(CBranch_patcherDlg) m_SrcDir = _T(""); m_DestDir = _T(""); m_Filename = _T(""); m_Tokens = _T(""); m_SrcDirLabel = _T(""); m_TargetDirLabel = _T(""); //}}AFX_DATA_INIT m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_Display = NULL; } void CBranch_patcherDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CBranch_patcherDlg) DDX_Text(pDX, IDC_SRCDIR, m_SrcDir); DDX_Text(pDX, IDC_DESTDIR, m_DestDir); DDX_Text(pDX, IDC_Filename, m_Filename); DDX_Text(pDX, IDC_CurrentTokens, m_Tokens); DDX_Text(pDX, IDC_SrcDirLabel, m_SrcDirLabel); DDX_Text(pDX, IDC_TargetDirLabel, m_TargetDirLabel); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CBranch_patcherDlg, CDialog) //{{AFX_MSG_MAP(CBranch_patcherDlg) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_ButtonSetSrcDir, OnButtonSetSrcDir) ON_BN_CLICKED(IDC_ButtonSetDestDir, OnButtonSetDestDir) ON_BN_CLICKED(IDC_ButtonPatch, OnButtonPatch) ON_BN_CLICKED(IDC_DoPatch, OnDoPatch) ON_WM_SIZE() ON_WM_CLOSE() ON_BN_CLICKED(IDC_ButtonExtractTokens, OnButtonExtractTokens) ON_BN_CLICKED(IDC_ButtonClearTokens, OnButtonClearTokens) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CBranch_patcherDlg message handlers BOOL CBranch_patcherDlg::OnInitDialog() { CDialog::OnInitDialog(); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // Extra initialization here RECT cltRect; GetClientRect( &cltRect ), m_Display = new CRichEditCtrl(); m_Display->Create( WS_CHILD|WS_VISIBLE|WS_BORDER|WS_HSCROLL|WS_VSCROLL|ES_AUTOHSCROLL|ES_AUTOVSCROLL|ES_MULTILINE, CRect( 20, 180, cltRect.right-20, cltRect.bottom-20 ), this, 1 ); // Initialize directories loadConfiguration(); processCommandLine(); displayTokens(); EnteringTokens = false; m_SrcDirLabel = "Source Dir"; m_TargetDirLabel = "Target Dir"; UpdateData( false ); ((CButton*)GetDlgItem( IDC_DoPatch ))->EnableWindow( FALSE ); return TRUE; // return TRUE unless you set the focus to a control } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CBranch_patcherDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } HCURSOR CBranch_patcherDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CBranch_patcherDlg::setSrcDirectory( const CString& s ) { m_SrcDir = s; UpdateData( false ); } void CBranch_patcherDlg::setDestDirectory( const CString& s ) { m_DestDir = s; UpdateData( false ); } void CBranch_patcherDlg::OnButtonSetSrcDir() { DirDialog.m_strTitle = "Please choose the SOURCE directory"; if ( DirDialog.DoBrowse() == TRUE ) { setSrcDirectory( DirDialog.m_strPath ); guessDestDirectory(); } } void CBranch_patcherDlg::OnButtonSetDestDir() { DirDialog.m_strTitle = "Please choose the TARGET directory"; if ( DirDialog.DoBrowse() == TRUE ) { setDestDirectory( DirDialog.m_strPath ); } } CDirDialog::CDirDialog() {//////////////////////////////////////////// } CDirDialog::~CDirDialog() {/////////////////////////////////////////// } int CDirDialog::DoBrowse () {///////////////////////////////////////// LPMALLOC pMalloc; if (SHGetMalloc (&pMalloc)!= NOERROR) { return 0; } BROWSEINFO bInfo; LPITEMIDLIST pidl; ZeroMemory ( (PVOID) &bInfo,sizeof (BROWSEINFO)); if (!m_strInitDir.IsEmpty ()) { OLECHAR olePath[MAX_PATH]; ULONG chEaten; ULONG dwAttributes; HRESULT hr; LPSHELLFOLDER pDesktopFolder; // // Get a pointer to the Desktop's IShellFolder interface. // if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder))) { // // IShellFolder::ParseDisplayName requires the file name be in Unicode. // MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strInitDir.GetBuffer (MAX_PATH), -1, olePath, MAX_PATH); m_strInitDir.ReleaseBuffer (-1); // // Convert the path to an ITEMIDLIST. // hr = pDesktopFolder->ParseDisplayName(NULL, NULL, olePath, &chEaten, &pidl, &dwAttributes); if (FAILED(hr)) { pMalloc ->Free (pidl); pMalloc ->Release (); return 0; } bInfo.pidlRoot = pidl; } } bInfo.hwndOwner = NULL; bInfo.pszDisplayName = m_strPath.GetBuffer (MAX_PATH); bInfo.lpszTitle = (m_strTitle.IsEmpty()) ? "Open":m_strTitle; bInfo.ulFlags = BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS; if ((pidl = ::SHBrowseForFolder (&bInfo)) == NULL) { return 0; } m_strPath.ReleaseBuffer (); m_iImageIndex = bInfo.iImage; if (::SHGetPathFromIDList(pidl,m_strPath.GetBuffer (MAX_PATH)) == FALSE) { pMalloc ->Free (pidl); pMalloc ->Release (); return 0; } m_strPath.ReleaseBuffer (); pMalloc ->Free (pidl); pMalloc ->Release (); return 1; } /* * Adapted from function by Jonah Bishop <jonahb@nc.rr.com> */ BOOL SendTextToClipboard(CString source) { // Return value is TRUE if the text was sent // Return value is FALSE if something went wrong if(OpenClipboard(NULL)) { HGLOBAL clipbuffer; char* buffer; EmptyClipboard(); // Empty whatever's already there clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.GetLength()+1); buffer = (char*)GlobalLock(clipbuffer); strcpy(buffer, LPCSTR(source)); GlobalUnlock(clipbuffer); SetClipboardData(CF_TEXT, clipbuffer); // Send the data CloseClipboard(); // VERY IMPORTANT return TRUE; } return FALSE; } void CBranch_patcherDlg::displayMessage( const CString& msg, bool insertAtTop ) { if ( insertAtTop ) m_Display->SetSel( 0, 0 ); else m_Display->SetSel( 0, -1 ); m_Display->ReplaceSel( msg ); SaveDiff = false; } void CBranch_patcherDlg::OnButtonPatch() { UpdateData( true ); CString diffCmdLine; diffCmdLine.Format( "cvs.exe diff -c > %s 2> %s", TEMP_DIFF_FILE, DIFF_ERRORS ); // needs a valid cvs login before! and cvs.exe in the path CString text; text.Format( "Get diff from directory %s?\n\nCommand (choose No to copy it into the clipboard):\n%s", m_SrcDir, diffCmdLine ); int result; if ( (result = ::MessageBox( m_hWnd, text, "Confirmation", MB_YESNOCANCEL | MB_ICONQUESTION )) == IDYES ) { if ( _chdir( m_SrcDir ) == 0 ) { system( diffCmdLine ); displayFile( TEMP_DIFF_FILE ); SaveDiff = true; colorizeDiff(); m_Display->LineScroll( 0 ); ((CButton*)GetDlgItem( IDC_DoPatch ))->EnableWindow( TRUE ); if ( (m_Display->GetLineCount() == 0) || (m_Display->GetLineCount() == 1 && m_Display->LineLength(0)<2) ) { displayFile( DIFF_ERRORS ); displayMessage( "Diff is empty.\r\nIf this is not the expected result:\r\n- check if the source directory is part of a CVS tree\r\n- check if cvs.exe is in your PATH\r\n- check if you are logged to the cvs server with 'cvs login' (set your home cvs directory in the HOME environment variable if needed)\r\n- check if C:\\ has enough free space and access rights to write a file.\n\nHere is the log:\n\n", true ); } else { m_Filename = TEMP_DIFF_FILE + ":"; UpdateData( false ); } } else { displayMessage( "Source directory not found" ); } } else if ( result == IDNO ) { SendTextToClipboard( diffCmdLine ); } } static unsigned long CALLBACK MyStreamInCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { CFile* pFile = (CFile*) dwCookie; *pcb = pFile->Read(pbBuff, cb); return 0; } static unsigned long CALLBACK MyStreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) { CFile* pFile = (CFile*) dwCookie; pFile->Write(pbBuff, cb); *pcb = cb; return 0; } void CBranch_patcherDlg::displayFile( const CString& filename ) { CFile cFile( filename, CFile::modeRead ); EDITSTREAM es; es.dwCookie = (DWORD_PTR) &cFile; es.pfnCallback = MyStreamInCallback; m_Display->StreamIn( SF_TEXT, es ); } void CBranch_patcherDlg::saveFile( const CString& filename ) { CFile cFile( filename, CFile::modeCreate | CFile::modeWrite ); EDITSTREAM es; es.dwCookie = (DWORD_PTR) &cFile; es.pfnCallback = MyStreamOutCallback; m_Display->StreamOut( SF_TEXT, es ); } void CBranch_patcherDlg::colorizeDiff() { CHARFORMAT blue; ZeroMemory( &blue, sizeof(blue) ); blue.cbSize = sizeof(blue); blue.dwMask = CFM_COLOR; blue.crTextColor = RGB(0,0,0xFF); CHARFORMAT red; ZeroMemory( &red, sizeof(red) ); red.cbSize = sizeof(red); red.dwMask = CFM_COLOR; red.crTextColor = RGB(0xFF,0,0); CHARFORMAT green; ZeroMemory( &green, sizeof(green) ); green.cbSize = sizeof(green); green.dwMask = CFM_COLOR; green.crTextColor = RGB(0,0x7F,0); for ( int i=0; i!=m_Display->GetLineCount(); ++i ) { int c = m_Display->LineIndex( i ); int l = m_Display->LineLength( c ); m_Display->SetSel( c, c+l ); CString s = m_Display->GetSelText(); if ( ! s.IsEmpty() ) { if ( s.Left(2) == "+ " ) { m_Display->SetSelectionCharFormat( blue ); } else if ( s.Left(2) == "- " ) { m_Display->SetSelectionCharFormat( red ); } else if ( s.Left(2) == "! " ) { m_Display->SetSelectionCharFormat( green ); } } } } void CBranch_patcherDlg::OnDoPatch() { UpdateData( true ); if ( SaveDiff ) { // Save the diff from the richedit saveFile( TEMP_DIFF_FILE ); } // Apply the patch CString patchCmdLine, concatOutput, delPatchErrors; patchCmdLine.Format( "%spatch.exe -c -p%u --verbose < %s > %s 2> %s", PatchExeDir, CvsDiffDirLevel, TEMP_DIFF_FILE, PATCH_RESULT, PATCH_ERRORS ); // needs patch.exe in the path concatOutput.Format( "copy %s+%s %s", PATCH_RESULT, PATCH_ERRORS, PATCH_RESULT ); delPatchErrors.Format( "del %s", PATCH_ERRORS ); CString text; text.Format( "Patch diff to directory %s?\n\nCommand (choose No to copy it into the clipboard):\n%s", m_DestDir, patchCmdLine ); int result; if ( (result = ::MessageBox( m_hWnd, text, "Confirmation", MB_YESNOCANCEL | MB_ICONQUESTION )) == IDYES ) { if ( _chdir( m_DestDir ) == 0 ) { system( patchCmdLine ); system( concatOutput ); system( delPatchErrors ); displayFile( PATCH_RESULT ); SaveDiff = false; m_Display->LineScroll( 0 ); if ( (m_Display->GetLineCount() == 0) || (m_Display->GetLineCount() == 1 && m_Display->LineLength(0)<2) ) { CString s; s.Format( "Nothing was patched.\r\nIf this is not the expected result:\r\n- check if the good patch.exe is in %s\r\n- check if %s exists (generated by previous diff)\r\n- check if C:\\ has enough free space and access rights to write a file.", TEMP_DIFF_FILE ); displayMessage( s ); } else { m_Filename = PATCH_RESULT + ":"; UpdateData( false ); } } else { displayMessage( "Target directory not found" ); } } else if ( result == IDNO ) { SendTextToClipboard( patchCmdLine ); } } void CBranch_patcherDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); if ( m_Display ) { RECT cltRect; GetClientRect( &cltRect ); CRect dispRect; m_Display->MoveWindow( 20, 180, cltRect.right-40, cltRect.bottom-200, true ); } } void CBranch_patcherDlg::OnClose() { saveConfiguration(); CDialog::OnClose(); } void CBranch_patcherDlg::processCommandLine() { CString cmdLine = theApp.m_lpCmdLine; if ( ! cmdLine.IsEmpty() ) { setSrcDirectory( cmdLine ); guessDestDirectory(); } } void CBranch_patcherDlg::guessDestDirectory() { if ( hasTokens() ) { CString dir = m_SrcDir; if ( dir.Find( "\\"+Token1+"\\", 0 ) != -1 ) { dir.Replace( "\\"+Token1+"\\", "\\"+Token2+"\\" ); setDestDirectory( dir ); } else if ( dir.Find( "\\"+Token2+"\\", 0 ) != -1 ) { dir.Replace( "\\"+Token2+"\\", "\\"+Token1+"\\" ); setDestDirectory( dir ); } } } void CBranch_patcherDlg::extractDirTokens() { int beginOfToken1, beginOfToken2, endOfToken1, endOfToken2; CString text; UpdateData( true ); // Search backward from the end until a different substring is found int c1 = m_SrcDir.GetLength()-1; int c2 = m_DestDir.GetLength()-1; while ( (c1 >= 0) && (c2 >= 0) && (m_SrcDir[c1] == m_DestDir[c2]) ) { --c1; --c2; } // Test if both strings are identical if ( (c1 < 0) || (c2 < 0) ) { Token1 = m_SrcDir; Token2 = m_DestDir; return; } endOfToken1 = c1+1; endOfToken2 = c2+1; // Search forward from the beginning until a different substring is found c1 = 0; c2 = 0; while ( (c1 < m_SrcDir.GetLength()) && (c2 < m_DestDir.GetLength()) && (m_SrcDir[c1] == m_DestDir[c2]) ) { ++c1; ++c2; } if ( (c1 == m_SrcDir.GetLength()) || (c2 == m_DestDir.GetLength()) ) { return; // both strings are identical (should not occur again) } // If one of the token is empty, expand both downto the closest backslash if ( (c1 == endOfToken1) || (c2 == endOfToken2) ) { --c1; while ( (c1 >= 0) && (m_SrcDir[c1] != '\\') ) { --c1; } ++c1; --c2; while ( (c2 >= 0) && (m_DestDir[c2] != '\\') ) { --c2; } ++c2; } beginOfToken1 = c1; beginOfToken2 = c2; Token1 = m_SrcDir.Mid( beginOfToken1, endOfToken1-beginOfToken1 ); Token2 = m_DestDir.Mid( beginOfToken2, endOfToken2-beginOfToken2 ); //endExtract: /*if ( hasTokens() ) { text.Format( "The two branch tokens '%s' and '%s' are now stored", Token1, Token2 ); ::MessageBox( m_hWnd, text, "Tokens found", MB_OK | MB_ICONINFORMATION ); return; }*/ //notfound: //::MessageBox( m_hWnd, "Tokens not found in the directories", "Extracting tokens", MB_OK | MB_ICONEXCLAMATION ); } void CBranch_patcherDlg::loadConfiguration() { // Read the dest directory from the registry free( (void*)theApp.m_pszRegistryKey ); theApp.m_pszRegistryKey = _tcsdup( _T("Nevrax") ); CString savedSrcDir, savedTargetDir, token1, token2; if ( m_SrcDir.IsEmpty() ) { savedSrcDir = theApp.GetProfileString( _T(""), _T("SourceDir") ); if ( ! savedSrcDir.IsEmpty() ) { setSrcDirectory( savedSrcDir ); } } savedTargetDir = theApp.GetProfileString( _T(""), _T("TargetDir") ); if ( ! savedTargetDir.IsEmpty() ) { setDestDirectory( savedTargetDir ); } Token1 = theApp.GetProfileString( _T(""), _T("Token1") ); Token2 = theApp.GetProfileString( _T(""), _T("Token2") ); PatchExeDir = theApp.GetProfileString( _T(""), _T("PatchExeDir") ); CvsDiffDirLevel = theApp.GetProfileInt( _T(""), _T("CvsDiffDirLevel"), 1 ); // 0 for old version of CVS, 1 for new version of CVS } void CBranch_patcherDlg::saveConfiguration() { UpdateData( true ); if ( ! EnteringTokens ) { theApp.WriteProfileString( _T(""), _T("SourceDir"), m_SrcDir ); theApp.WriteProfileString( _T(""), _T("TargetDir"), m_DestDir ); } theApp.WriteProfileString( _T(""), _T("Token1"), Token1 ); theApp.WriteProfileString( _T(""), _T("Token2"), Token2 ); } void CBranch_patcherDlg::OnButtonExtractTokens() { if ( ! EnteringTokens ) { EnteringTokens = true; extractDirTokens(); SrcDirBackup = m_SrcDir; TargetDirBackup = m_DestDir; m_SrcDir = Token1; m_DestDir = Token2; m_SrcDirLabel = "Enter Token 1"; m_TargetDirLabel = "Enter Token 2"; m_Filename = "The tokens above were extracted from the directories."; ((CButton*)GetDlgItem( IDC_ButtonExtractTokens ))->SetWindowText( "Store Tokens" ); GetDlgItem( IDC_TopText )->ShowWindow( SW_HIDE ); GetDlgItem( IDC_ButtonClearTokens )->EnableWindow( FALSE ); GetDlgItem( IDC_ButtonPatch )->ShowWindow( SW_HIDE ); GetDlgItem( IDC_ButtonPatch )->EnableWindow( FALSE ); GetDlgItem( IDC_DoPatch )->ShowWindow( SW_HIDE ); GetDlgItem( IDC_Group )->ShowWindow( SW_HIDE ); UpdateData( false ); } else { UpdateData( true ); EnteringTokens = false; Token1 = m_SrcDir; Token2 = m_DestDir; m_SrcDirLabel = "Source Dir"; m_TargetDirLabel = "Target Dir"; m_SrcDir = SrcDirBackup; m_DestDir = TargetDirBackup; m_Filename = ""; ((CButton*)GetDlgItem( IDC_ButtonExtractTokens ))->SetWindowText( "Enter Tokens" ); GetDlgItem( IDC_TopText )->ShowWindow( SW_SHOW ); GetDlgItem( IDC_ButtonClearTokens )->EnableWindow( TRUE ); GetDlgItem( IDC_ButtonPatch )->ShowWindow( SW_SHOW ); GetDlgItem( IDC_ButtonPatch )->EnableWindow( TRUE ); GetDlgItem( IDC_DoPatch )->ShowWindow( SW_SHOW ); GetDlgItem( IDC_Group )->ShowWindow( SW_SHOW ); displayTokens(); } } void CBranch_patcherDlg::OnButtonClearTokens() { Token1 = ""; Token2 = ""; displayTokens(); } bool CBranch_patcherDlg::hasTokens() const { return ! (Token1.IsEmpty() || Token2.IsEmpty()); } void CBranch_patcherDlg::displayTokens() { ((CButton*)GetDlgItem( IDC_ButtonClearTokens ))->EnableWindow( hasTokens()?TRUE:FALSE ); if ( hasTokens() ) { m_Tokens = "Tokens: '" + Token1 + "' and '" + Token2 + "'"; } else { m_Tokens = "No token"; } UpdateData( false ); }