khanat-opennel-code/code/nel/tools/misc/branch_patcher/branch_patcherDlg.cpp
2011-08-13 17:39:55 +02:00

730 lines
20 KiB
C++

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