1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // branch_patcherDlg.cpp : implementation file
24 #include "branch_patcher.h"
25 #include "branch_patcherDlg.h"
32 static char THIS_FILE
[] = __FILE__
;
35 /////////////////////////////////////////////////////////////////////////////
36 // CBranch_patcherDlg dialog
38 extern CBranch_patcherApp theApp
;
40 const CString TEMP_DIFF_FILE
= "C:\\tempFile.diff";
41 const CString DIFF_ERRORS
= "C:\\diffLog.txt";
42 const CString PATCH_RESULT
= "C:\\patchResult.txt";
43 const CString PATCH_ERRORS
= "C:\\patchErrors.txt";
46 CBranch_patcherDlg::CBranch_patcherDlg(CWnd
* pParent
/*=NULL*/)
47 : CDialog(CBranch_patcherDlg::IDD
, pParent
)
49 //{{AFX_DATA_INIT(CBranch_patcherDlg)
54 m_SrcDirLabel
= _T("");
55 m_TargetDirLabel
= _T("");
57 m_hIcon
= AfxGetApp()->LoadIcon(IDR_MAINFRAME
);
61 void CBranch_patcherDlg::DoDataExchange(CDataExchange
* pDX
)
63 CDialog::DoDataExchange(pDX
);
64 //{{AFX_DATA_MAP(CBranch_patcherDlg)
65 DDX_Text(pDX
, IDC_SRCDIR
, m_SrcDir
);
66 DDX_Text(pDX
, IDC_DESTDIR
, m_DestDir
);
67 DDX_Text(pDX
, IDC_Filename
, m_Filename
);
68 DDX_Text(pDX
, IDC_CurrentTokens
, m_Tokens
);
69 DDX_Text(pDX
, IDC_SrcDirLabel
, m_SrcDirLabel
);
70 DDX_Text(pDX
, IDC_TargetDirLabel
, m_TargetDirLabel
);
74 BEGIN_MESSAGE_MAP(CBranch_patcherDlg
, CDialog
)
75 //{{AFX_MSG_MAP(CBranch_patcherDlg)
78 ON_BN_CLICKED(IDC_ButtonSetSrcDir
, OnButtonSetSrcDir
)
79 ON_BN_CLICKED(IDC_ButtonSetDestDir
, OnButtonSetDestDir
)
80 ON_BN_CLICKED(IDC_ButtonPatch
, OnButtonPatch
)
81 ON_BN_CLICKED(IDC_DoPatch
, OnDoPatch
)
84 ON_BN_CLICKED(IDC_ButtonExtractTokens
, OnButtonExtractTokens
)
85 ON_BN_CLICKED(IDC_ButtonClearTokens
, OnButtonClearTokens
)
89 /////////////////////////////////////////////////////////////////////////////
90 // CBranch_patcherDlg message handlers
92 BOOL
CBranch_patcherDlg::OnInitDialog()
94 CDialog::OnInitDialog();
96 SetIcon(m_hIcon
, TRUE
); // Set big icon
97 SetIcon(m_hIcon
, FALSE
); // Set small icon
99 // Extra initialization here
102 GetClientRect(&cltRect
),
103 m_Display
= new CRichEditCtrl();
104 m_Display
->Create(WS_CHILD
| WS_VISIBLE
| WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
| ES_AUTOHSCROLL
| ES_AUTOVSCROLL
| ES_MULTILINE
,
105 CRect(20, 180, cltRect
.right
- 20, cltRect
.bottom
- 20), this, 1);
107 // Initialize directories
109 processCommandLine();
112 EnteringTokens
= false;
113 m_SrcDirLabel
= "Source Dir";
114 m_TargetDirLabel
= "Target Dir";
116 ((CButton
*)GetDlgItem(IDC_DoPatch
))->EnableWindow(FALSE
);
118 return TRUE
; // return TRUE unless you set the focus to a control
121 // If you add a minimize button to your dialog, you will need the code below
122 // to draw the icon. For MFC applications using the document/view model,
123 // this is automatically done for you by the framework.
125 void CBranch_patcherDlg::OnPaint()
129 CPaintDC
dc(this); // device context for painting
131 SendMessage(WM_ICONERASEBKGND
, (WPARAM
)dc
.GetSafeHdc(), 0);
133 // Center icon in client rectangle
134 int cxIcon
= GetSystemMetrics(SM_CXICON
);
135 int cyIcon
= GetSystemMetrics(SM_CYICON
);
137 GetClientRect(&rect
);
138 int x
= (rect
.Width() - cxIcon
+ 1) / 2;
139 int y
= (rect
.Height() - cyIcon
+ 1) / 2;
142 dc
.DrawIcon(x
, y
, m_hIcon
);
150 HCURSOR
CBranch_patcherDlg::OnQueryDragIcon()
152 return (HCURSOR
)m_hIcon
;
156 void CBranch_patcherDlg::setSrcDirectory(const CString
& s
)
162 void CBranch_patcherDlg::setDestDirectory(const CString
& s
)
168 void CBranch_patcherDlg::OnButtonSetSrcDir()
170 DirDialog
.m_strTitle
= "Please choose the SOURCE directory";
171 if (DirDialog
.DoBrowse() == TRUE
)
173 setSrcDirectory(DirDialog
.m_strPath
);
174 guessDestDirectory();
178 void CBranch_patcherDlg::OnButtonSetDestDir()
180 DirDialog
.m_strTitle
= "Please choose the TARGET directory";
181 if (DirDialog
.DoBrowse() == TRUE
)
183 setDestDirectory(DirDialog
.m_strPath
);
194 CDirDialog::CDirDialog()
195 {////////////////////////////////////////////
199 CDirDialog::~CDirDialog()
200 {///////////////////////////////////////////
204 int CDirDialog::DoBrowse()
205 {/////////////////////////////////////////
208 if (SHGetMalloc(&pMalloc
) != NOERROR
)
215 ZeroMemory((PVOID
)&bInfo
, sizeof(BROWSEINFO
));
217 if (!m_strInitDir
.IsEmpty())
219 OLECHAR olePath
[MAX_PATH
];
220 ULONG dwAttributes
= 0;
222 LPSHELLFOLDER pDesktopFolder
;
223 // // Get a pointer to the Desktop's IShellFolder interface. //
224 if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder
)))
228 // IShellFolder::ParseDisplayName requires the file name be in Unicode.
230 MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, m_strInitDir
.GetBuffer(MAX_PATH
), -1, olePath
, MAX_PATH
);
232 m_strInitDir
.ReleaseBuffer(-1);
234 wcscpy(olePath
, (LPCTSTR
)m_strInitDir
);
237 // Convert the path to an ITEMIDLIST.
239 hr
= pDesktopFolder
->ParseDisplayName(NULL
,
253 bInfo
.pidlRoot
= pidl
;
257 bInfo
.hwndOwner
= NULL
;
258 bInfo
.pszDisplayName
= m_strPath
.GetBuffer(MAX_PATH
);
259 bInfo
.lpszTitle
= (m_strTitle
.IsEmpty()) ? "Open" : m_strTitle
;
260 bInfo
.ulFlags
= BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
262 if ((pidl
= ::SHBrowseForFolder(&bInfo
)) == NULL
)
267 m_strPath
.ReleaseBuffer();
268 m_iImageIndex
= bInfo
.iImage
;
270 if (::SHGetPathFromIDList(pidl
, m_strPath
.GetBuffer(MAX_PATH
)) == FALSE
)
277 m_strPath
.ReleaseBuffer();
286 * Adapted from function by Jonah Bishop <jonahb@nc.rr.com>
288 BOOL
SendTextToClipboard(CString source
)
290 // Return value is TRUE if the text was sent
291 // Return value is FALSE if something went wrong
292 if (OpenClipboard(NULL
))
297 EmptyClipboard(); // Empty whatever's already there
299 clipbuffer
= GlobalAlloc(GMEM_DDESHARE
, ((SIZE_T
)source
.GetLength() + 1) * sizeof(TCHAR
));
302 buffer
= (TCHAR
*)GlobalLock(clipbuffer
);
305 _tcscpy(buffer
, LPCTSTR(source
));
306 GlobalUnlock(clipbuffer
);
308 SetClipboardData(CF_TEXT
, clipbuffer
); // Send the data
310 CloseClipboard(); // VERY IMPORTANT
314 CloseClipboard(); // VERY IMPORTANT
320 void CBranch_patcherDlg::displayMessage(const CString
& msg
, bool insertAtTop
)
323 m_Display
->SetSel(0, 0);
325 m_Display
->SetSel(0, -1);
326 m_Display
->ReplaceSel(msg
);
331 void CBranch_patcherDlg::OnButtonPatch()
336 diffCmdLine
.Format(_T("cvs.exe diff -c > %s 2> %s"), TEMP_DIFF_FILE
, DIFF_ERRORS
); // needs a valid cvs login before! and cvs.exe in the path
338 text
.Format(_T("Get diff from directory %s?\n\nCommand (choose No to copy it into the clipboard):\n%s"), m_SrcDir
, diffCmdLine
);
340 if ((result
= ::MessageBox(m_hWnd
, text
, _T("Confirmation"), MB_YESNOCANCEL
| MB_ICONQUESTION
)) == IDYES
)
342 if (_tchdir(m_SrcDir
) == 0)
344 _tsystem(diffCmdLine
);
345 displayFile(TEMP_DIFF_FILE
);
348 m_Display
->LineScroll(0);
349 ((CButton
*)GetDlgItem(IDC_DoPatch
))->EnableWindow(TRUE
);
351 if ((m_Display
->GetLineCount() == 0) ||
352 (m_Display
->GetLineCount() == 1 && m_Display
->LineLength(0) < 2))
354 displayFile(DIFF_ERRORS
);
355 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);
359 m_Filename
= TEMP_DIFF_FILE
+ ":";
365 displayMessage("Source directory not found");
368 else if (result
== IDNO
)
370 SendTextToClipboard(diffCmdLine
);
375 static unsigned long CALLBACK
MyStreamInCallback(DWORD_PTR dwCookie
, LPBYTE pbBuff
, LONG cb
, LONG
*pcb
)
377 CFile
* pFile
= (CFile
*)dwCookie
;
378 *pcb
= pFile
->Read(pbBuff
, cb
);
383 static unsigned long CALLBACK
MyStreamOutCallback(DWORD_PTR dwCookie
, LPBYTE pbBuff
, LONG cb
, LONG
*pcb
)
385 CFile
* pFile
= (CFile
*)dwCookie
;
386 pFile
->Write(pbBuff
, cb
);
392 void CBranch_patcherDlg::displayFile(const CString
& filename
)
394 CFile
cFile(filename
, CFile::modeRead
);
396 es
.dwCookie
= (DWORD_PTR
)&cFile
;
397 es
.pfnCallback
= MyStreamInCallback
;
398 m_Display
->StreamIn(SF_TEXT
, es
);
402 void CBranch_patcherDlg::saveFile(const CString
& filename
)
404 CFile
cFile(filename
, CFile::modeCreate
| CFile::modeWrite
);
406 es
.dwCookie
= (DWORD_PTR
)&cFile
;
407 es
.pfnCallback
= MyStreamOutCallback
;
408 m_Display
->StreamOut(SF_TEXT
, es
);
412 void CBranch_patcherDlg::colorizeDiff()
415 ZeroMemory(&blue
, sizeof(blue
));
416 blue
.cbSize
= sizeof(blue
);
417 blue
.dwMask
= CFM_COLOR
;
418 blue
.crTextColor
= RGB(0, 0, 0xFF);
420 ZeroMemory(&red
, sizeof(red
));
421 red
.cbSize
= sizeof(red
);
422 red
.dwMask
= CFM_COLOR
;
423 red
.crTextColor
= RGB(0xFF, 0, 0);
425 ZeroMemory(&green
, sizeof(green
));
426 green
.cbSize
= sizeof(green
);
427 green
.dwMask
= CFM_COLOR
;
428 green
.crTextColor
= RGB(0, 0x7F, 0);
429 for (int i
= 0; i
!= m_Display
->GetLineCount(); ++i
)
431 int c
= m_Display
->LineIndex(i
);
432 int l
= m_Display
->LineLength(c
);
433 m_Display
->SetSel(c
, c
+ l
);
434 CString s
= m_Display
->GetSelText();
437 if (s
.Left(2) == "+ ")
439 m_Display
->SetSelectionCharFormat(blue
);
441 else if (s
.Left(2) == "- ")
443 m_Display
->SetSelectionCharFormat(red
);
445 else if (s
.Left(2) == "! ")
447 m_Display
->SetSelectionCharFormat(green
);
454 void CBranch_patcherDlg::OnDoPatch()
460 // Save the diff from the richedit
461 saveFile(TEMP_DIFF_FILE
);
465 CString patchCmdLine
, concatOutput
, delPatchErrors
;
466 patchCmdLine
.Format(_T("%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
467 concatOutput
.Format(_T("copy %s+%s %s"), PATCH_RESULT
, PATCH_ERRORS
, PATCH_RESULT
);
468 delPatchErrors
.Format(_T("del %s"), PATCH_ERRORS
);
471 text
.Format(_T("Patch diff to directory %s?\n\nCommand (choose No to copy it into the clipboard):\n%s"), (LPCTSTR
)m_DestDir
, (LPCTSTR
)patchCmdLine
);
473 if ((result
= ::MessageBox(m_hWnd
, text
, _T("Confirmation"), MB_YESNOCANCEL
| MB_ICONQUESTION
)) == IDYES
)
475 if (_tchdir(m_DestDir
) == 0)
477 _tsystem(patchCmdLine
);
478 _tsystem(concatOutput
);
479 _tsystem(delPatchErrors
);
480 displayFile(PATCH_RESULT
);
482 m_Display
->LineScroll(0);
484 if ((m_Display
->GetLineCount() == 0) ||
485 (m_Display
->GetLineCount() == 1 && m_Display
->LineLength(0) < 2))
488 s
.Format(_T("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
);
493 m_Filename
= PATCH_RESULT
+ ":";
499 displayMessage("Target directory not found");
502 else if (result
== IDNO
)
504 SendTextToClipboard(patchCmdLine
);
509 void CBranch_patcherDlg::OnSize(UINT nType
, int cx
, int cy
)
511 CDialog::OnSize(nType
, cx
, cy
);
516 GetClientRect(&cltRect
);
518 m_Display
->MoveWindow(20, 180, cltRect
.right
- 40, cltRect
.bottom
- 200, true);
522 void CBranch_patcherDlg::OnClose()
530 void CBranch_patcherDlg::processCommandLine()
532 CString cmdLine
= theApp
.m_lpCmdLine
;
534 if (!cmdLine
.IsEmpty())
536 setSrcDirectory(cmdLine
);
537 guessDestDirectory();
542 void CBranch_patcherDlg::guessDestDirectory()
546 CString dir
= m_SrcDir
;
547 if (dir
.Find("\\" + Token1
+ "\\", 0) != -1)
549 dir
.Replace("\\" + Token1
+ "\\", "\\" + Token2
+ "\\");
550 setDestDirectory(dir
);
552 else if (dir
.Find("\\" + Token2
+ "\\", 0) != -1)
554 dir
.Replace("\\" + Token2
+ "\\", "\\" + Token1
+ "\\");
555 setDestDirectory(dir
);
561 void CBranch_patcherDlg::extractDirTokens()
563 int beginOfToken1
, beginOfToken2
, endOfToken1
, endOfToken2
;
567 // Search backward from the end until a different substring is found
568 int c1
= m_SrcDir
.GetLength() - 1;
569 int c2
= m_DestDir
.GetLength() - 1;
570 while ((c1
>= 0) && (c2
>= 0) && (m_SrcDir
[c1
] == m_DestDir
[c2
]))
576 // Test if both strings are identical
577 if ((c1
< 0) || (c2
< 0))
583 endOfToken1
= c1
+ 1;
584 endOfToken2
= c2
+ 1;
586 // Search forward from the beginning until a different substring is found
589 while ((c1
< m_SrcDir
.GetLength()) && (c2
< m_DestDir
.GetLength()) && (m_SrcDir
[c1
] == m_DestDir
[c2
]))
594 if ((c1
== m_SrcDir
.GetLength()) || (c2
== m_DestDir
.GetLength()))
596 return; // both strings are identical (should not occur again)
599 // If one of the token is empty, expand both downto the closest backslash
600 if ((c1
== endOfToken1
) || (c2
== endOfToken2
))
603 while ((c1
>= 0) && (m_SrcDir
[c1
] != '\\'))
609 while ((c2
>= 0) && (m_DestDir
[c2
] != '\\'))
618 Token1
= m_SrcDir
.Mid(beginOfToken1
, endOfToken1
- beginOfToken1
);
619 Token2
= m_DestDir
.Mid(beginOfToken2
, endOfToken2
- beginOfToken2
);
624 text.Format( "The two branch tokens '%s' and '%s' are now stored", Token1, Token2 );
625 ::MessageBox( m_hWnd, text, "Tokens found", MB_OK | MB_ICONINFORMATION );
629 //::MessageBox( m_hWnd, "Tokens not found in the directories", "Extracting tokens", MB_OK | MB_ICONEXCLAMATION );
633 void CBranch_patcherDlg::loadConfiguration()
635 // Read the dest directory from the registry
636 free((void*)theApp
.m_pszRegistryKey
);
637 theApp
.m_pszRegistryKey
= _tcsdup(_T("Nevrax"));
639 CString savedSrcDir
, savedTargetDir
, token1
, token2
;
640 if (m_SrcDir
.IsEmpty())
642 savedSrcDir
= theApp
.GetProfileString(_T(""), _T("SourceDir"));
643 if (!savedSrcDir
.IsEmpty())
645 setSrcDirectory(savedSrcDir
);
648 savedTargetDir
= theApp
.GetProfileString(_T(""), _T("TargetDir"));
649 if (!savedTargetDir
.IsEmpty())
651 setDestDirectory(savedTargetDir
);
653 Token1
= theApp
.GetProfileString(_T(""), _T("Token1"));
654 Token2
= theApp
.GetProfileString(_T(""), _T("Token2"));
655 PatchExeDir
= theApp
.GetProfileString(_T(""), _T("PatchExeDir"));
656 CvsDiffDirLevel
= theApp
.GetProfileInt(_T(""), _T("CvsDiffDirLevel"), 1); // 0 for old version of CVS, 1 for new version of CVS
660 void CBranch_patcherDlg::saveConfiguration()
665 theApp
.WriteProfileString(_T(""), _T("SourceDir"), m_SrcDir
);
666 theApp
.WriteProfileString(_T(""), _T("TargetDir"), m_DestDir
);
668 theApp
.WriteProfileString(_T(""), _T("Token1"), Token1
);
669 theApp
.WriteProfileString(_T(""), _T("Token2"), Token2
);
673 void CBranch_patcherDlg::OnButtonExtractTokens()
677 EnteringTokens
= true;
679 SrcDirBackup
= m_SrcDir
;
680 TargetDirBackup
= m_DestDir
;
683 m_SrcDirLabel
= "Enter Token 1";
684 m_TargetDirLabel
= "Enter Token 2";
685 m_Filename
= "The tokens above were extracted from the directories.";
686 ((CButton
*)GetDlgItem(IDC_ButtonExtractTokens
))->SetWindowText(_T("Store Tokens"));
687 GetDlgItem(IDC_TopText
)->ShowWindow(SW_HIDE
);
688 GetDlgItem(IDC_ButtonClearTokens
)->EnableWindow(FALSE
);
689 GetDlgItem(IDC_ButtonPatch
)->ShowWindow(SW_HIDE
);
690 GetDlgItem(IDC_ButtonPatch
)->EnableWindow(FALSE
);
691 GetDlgItem(IDC_DoPatch
)->ShowWindow(SW_HIDE
);
692 GetDlgItem(IDC_Group
)->ShowWindow(SW_HIDE
);
698 EnteringTokens
= false;
701 m_SrcDirLabel
= "Source Dir";
702 m_TargetDirLabel
= "Target Dir";
703 m_SrcDir
= SrcDirBackup
;
704 m_DestDir
= TargetDirBackup
;
706 ((CButton
*)GetDlgItem(IDC_ButtonExtractTokens
))->SetWindowText(_T("Enter Tokens"));
707 GetDlgItem(IDC_TopText
)->ShowWindow(SW_SHOW
);
708 GetDlgItem(IDC_ButtonClearTokens
)->EnableWindow(TRUE
);
709 GetDlgItem(IDC_ButtonPatch
)->ShowWindow(SW_SHOW
);
710 GetDlgItem(IDC_ButtonPatch
)->EnableWindow(TRUE
);
711 GetDlgItem(IDC_DoPatch
)->ShowWindow(SW_SHOW
);
712 GetDlgItem(IDC_Group
)->ShowWindow(SW_SHOW
);
718 void CBranch_patcherDlg::OnButtonClearTokens()
726 bool CBranch_patcherDlg::hasTokens() const
728 return !(Token1
.IsEmpty() || Token2
.IsEmpty());
732 void CBranch_patcherDlg::displayTokens()
734 ((CButton
*)GetDlgItem(IDC_ButtonClearTokens
))->EnableWindow(hasTokens() ? TRUE
: FALSE
);
737 m_Tokens
= "Tokens: '" + Token1
+ "' and '" + Token2
+ "'";
741 m_Tokens
= "No token";