OSDN Git Service

Make Unpacker plugins available for image compare and binary compare
authorTakashi Sawanaka <sdottaka@users.sourceforge.net>
Sun, 16 May 2021 00:19:32 +0000 (09:19 +0900)
committerTakashi Sawanaka <sdottaka@users.sourceforge.net>
Sun, 16 May 2021 00:19:32 +0000 (09:19 +0900)
13 files changed:
Src/FileTransform.cpp
Src/FileTransform.h
Src/HexMergeDoc.cpp
Src/HexMergeDoc.h
Src/HexMergeView.cpp
Src/HexMergeView.h
Src/IMergeDoc.h
Src/ImgMergeFrm.cpp
Src/ImgMergeFrm.h
Src/MainFrm.cpp
Src/Merge.cpp
Src/Merge.h
Src/MergeDoc.cpp

index 62d158e..3ab9562 100644 (file)
@@ -562,13 +562,13 @@ bool Interactive(String & text, const wchar_t *TransformationEvent, int iFncChos
        return (nChanged != 0);
 }
 
-String GetUnpackedFileExtension(const String& filepath, const PackingInfo* handler)
+String GetUnpackedFileExtension(const String& filteredFilenames, const PackingInfo* handler)
 {
        PluginInfo* plugin = nullptr;
        if (handler->m_PluginOrPredifferMode == PLUGIN_MODE::PLUGIN_MANUAL && !handler->m_PluginName.empty())
                plugin = CAllThreadsScripts::GetActiveSet()->GetPluginByName(nullptr, handler->m_PluginName.c_str());
        else
-               plugin = CAllThreadsScripts::GetActiveSet()->GetUnpackerPluginByFilter(filepath);
+               plugin = CAllThreadsScripts::GetActiveSet()->GetUnpackerPluginByFilter(filteredFilenames);
        return plugin ? plugin->m_ext : _T("");
 }
 
index 8248457..b42c2a9 100644 (file)
@@ -219,6 +219,6 @@ std::vector<String> GetFreeFunctionsInScripts(const wchar_t* TransformationEvent
  */
 bool Interactive(String & text, const wchar_t *TransformationEvent, int iFncChosen);
 
-String GetUnpackedFileExtension(const String& filepath, const PackingInfo* handler);
+String GetUnpackedFileExtension(const String& filteredFilenames, const PackingInfo* handler);
 
 }
index 56af264..02e3406 100644 (file)
@@ -332,7 +332,7 @@ void CHexMergeDoc::DoFileSave(int nBuffer)
        }
 }
 
-void CHexMergeDoc::DoFileSaveAs(int nBuffer)
+void CHexMergeDoc::DoFileSaveAs(int nBuffer, bool packing)
 {
        const String &path = m_filePaths.GetPath(nBuffer);
        String strPath;
@@ -345,7 +345,7 @@ void CHexMergeDoc::DoFileSaveAs(int nBuffer)
                title = _("Save Middle File As");
        if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
        {
-               if (Try(m_pView[nBuffer]->SaveFile(strPath.c_str())) == IDCANCEL)
+               if (Try(m_pView[nBuffer]->SaveFile(strPath.c_str(), packing)) == IDCANCEL)
                        return;
                if (path.empty())
                {
index feb1321..16f5833 100644 (file)
@@ -13,6 +13,7 @@
 #include "PathContext.h"
 #include "FileLocation.h"
 #include "IMergeDoc.h"
+#include "FileTransform.h"
 
 class CDirDoc;
 class CHexMergeFrame;
@@ -68,6 +69,9 @@ public:
        void DirDocClosing(CDirDoc * pDirDoc) override;
        bool CloseNow() override;
        bool GenerateReport(const String& sFileName) const override { return true; }
+       const PackingInfo* GetUnpacker() const { return &m_infoUnpacker; };
+       PackingInfo* GetUnpacker() { return &m_infoUnpacker; };
+       void SetUnpacker(const PackingInfo* infoUnpacker) override { if (infoUnpacker) m_infoUnpacker = *infoUnpacker;  };
        CHexMergeFrame * GetParentFrame() const;
        void UpdateHeaderPath(int pane);
        void RefreshOptions();
@@ -75,9 +79,10 @@ public:
        void MoveOnLoad(int nPane = -1, int nLineIndex = -1);
        void CheckFileChanged(void) override;
        String GetDescription(int pane) const { return m_strDesc[pane]; };
+       void SaveAs(int nBuffer, bool packing = true) { DoFileSaveAs(nBuffer, packing); }
 private:
        void DoFileSave(int nBuffer);
-       void DoFileSaveAs(int nBuffer);
+       void DoFileSaveAs(int nBuffer, bool packing = true);
        HRESULT LoadOneFile(int index, LPCTSTR filename, bool readOnly, const String& strDesc);
        void RecompareAs(UINT id);
 // Implementation data
@@ -86,6 +91,7 @@ protected:
        CDirDoc * m_pDirDoc;
        String m_strDesc[3]; /**< Left/right side description text */
        BUFFERTYPE m_nBufferType[3];
+       PackingInfo m_infoUnpacker;
 
 // Generated message map functions
 protected:
index f5a3114..f5c15ce 100644 (file)
@@ -16,6 +16,7 @@
 #include "Merge.h"
 #include "MainFrm.h"
 #include "HexMergeView.h"
+#include "HexMergeDoc.h"
 #include "OptionsDef.h"
 #include "OptionsMgr.h"
 #include "Environment.h"
@@ -96,6 +97,7 @@ END_MESSAGE_MAP()
 CHexMergeView::CHexMergeView()
 : m_pif(nullptr)
 , m_nThisPane(0)
+, m_unpackerSubcode(0)
 {
 }
 
@@ -267,7 +269,11 @@ IMergeDoc::FileChange CHexMergeView::IsFileChangedOnDisk(LPCTSTR path)
  */
 HRESULT CHexMergeView::LoadFile(LPCTSTR path)
 {
-       HANDLE h = CreateFile(path, GENERIC_READ,
+       CHexMergeDoc *pDoc = static_cast<CHexMergeDoc *>(GetDocument());
+       String strTempFileName = path;
+       if (!FileTransform::Unpacking(pDoc->GetUnpacker(), &m_unpackerSubcode, strTempFileName, path))
+               return E_FAIL;
+       HANDLE h = CreateFile(strTempFileName.c_str(), GENERIC_READ,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        HRESULT hr = SE(h != INVALID_HANDLE_VALUE);
@@ -297,7 +303,7 @@ HRESULT CHexMergeView::LoadFile(LPCTSTR path)
 /**
  * @brief Save file
  */
-HRESULT CHexMergeView::SaveFile(LPCTSTR path)
+HRESULT CHexMergeView::SaveFile(LPCTSTR path, bool packing)
 {
        // Warn user in case file has been changed by someone else
        if (IsFileChangedOnDisk(path) == IMergeDoc::FileChange::Changed)
@@ -337,9 +343,29 @@ HRESULT CHexMergeView::SaveFile(LPCTSTR path)
        CloseHandle(h);
        if (hr != S_OK)
                return hr;
-       hr = SE(CopyFile(sIntermediateFilename.c_str(), path, FALSE));
-       if (hr != S_OK)
-               return hr;
+
+       if (packing)
+       {
+               CHexMergeDoc* pDoc = static_cast<CHexMergeDoc*>(GetDocument());
+               if (!FileTransform::Packing(sIntermediateFilename, path, *pDoc->GetUnpacker(), m_unpackerSubcode))
+               {
+                       String str = CMergeApp::GetPackingErrorMessage(m_nThisPane, pDoc->m_nBuffers, path, pDoc->GetUnpacker()->m_PluginName);
+                       int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
+                       if (answer == IDOK)
+                       {
+                               pDoc->SaveAs(m_nThisPane, false);
+                               return S_OK;
+                       }
+                       return S_OK;
+               }
+       }
+       else
+       {
+               hr = SE(CopyFile(sIntermediateFilename.c_str(), path, FALSE));
+               if (hr != S_OK)
+                       return hr;
+       }
+
        m_fileInfo.Update(path);
        SetSavePoint();
        hr = SE(DeleteFile(sIntermediateFilename.c_str()));
index 13f3284..ad53b8f 100644 (file)
@@ -30,9 +30,10 @@ public:
 protected: // create from serialization only
        CHexMergeView();
        DECLARE_DYNCREATE(CHexMergeView)
+       int m_unpackerSubcode;
 public:
        HRESULT LoadFile(LPCTSTR);
-       HRESULT SaveFile(LPCTSTR);
+       HRESULT SaveFile(LPCTSTR, bool packing = true);
        IHexEditorWindow *GetInterface() const { return m_pif; }
        BYTE *GetBuffer(int);
        int GetLength();
index 75b307a..0b202a7 100644 (file)
@@ -3,6 +3,7 @@
 #include "UnicodeString.h"\r
 \r
 class CDirDoc;\r
+class PackingInfo;\r
 \r
 struct IMergeDoc\r
 {\r
@@ -19,5 +20,6 @@ struct IMergeDoc
        virtual bool GenerateReport(const String &path) const = 0;\r
        virtual void DirDocClosing(CDirDoc * pDirDoc) = 0;\r
        virtual void CheckFileChanged() = 0;\r
+       virtual void SetUnpacker(const PackingInfo *infoUnpacker) = 0;\r
 };\r
 \r
index 0d1c7ce..3c24d0f 100644 (file)
@@ -30,6 +30,7 @@
 #include "FileLocation.h"
 #include "Constants.h"
 #include "DropHandler.h"
+#include "Environment.h"
 #include <cmath>
 
 #ifdef _DEBUG
@@ -167,6 +168,7 @@ CImgMergeFrame::CImgMergeFrame()
 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
 , m_bRO{}
 , m_nActivePane(-1)
+, m_unpackerSubcode{}
 {
 }
 
@@ -264,19 +266,10 @@ void CImgMergeFrame::ChangeFile(int nBuffer, const String& path)
        m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
        m_strDesc[nBuffer] = _T("");
 
-       if (m_filePaths.GetSize() == 2)
-               m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
-       else
-               m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
-
+       OpenImages();
        for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
-       {
                m_fileInfo[pane].Update(m_filePaths[pane]);
 
-               RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
-                       new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
-       }
-
        UpdateHeaderPath(nBuffer);
        UpdateLastCompareResult();
 }
@@ -450,10 +443,7 @@ BOOL CImgMergeFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
        }
        else
        {
-               if (m_filePaths.GetSize() == 2)
-                       bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
-               else
-                       bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
+               bResult = OpenImages();
        }
 
        for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
@@ -691,6 +681,7 @@ bool CImgMergeFrame::DoFileSave(int pane)
                        if (CMergeApp::HandleReadonlySave(m_filePaths[pane], false, bApplyToAll) == IDCANCEL)
                                return false;
                        CMergeApp::CreateBackup(false, m_filePaths[pane]);
+                       int savepoint = m_pImgMergeWindow->GetSavePoint(pane);
                        if (!m_pImgMergeWindow->SaveImage(pane))
                        {
                                String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t- use a different filename (Press OK)\n\t- abort the current operation (Press Cancel)?"), filename, GetSysError());
@@ -699,6 +690,20 @@ bool CImgMergeFrame::DoFileSave(int pane)
                                        return DoFileSaveAs(pane);
                                return false;
                        }
+                       if (filename != m_filePaths[pane])
+                       {
+                               if (!FileTransform::Packing(filename, m_filePaths[pane], m_infoUnpacker, m_unpackerSubcode[pane]))
+                               {
+                                       // Restore save point
+                                       m_pImgMergeWindow->SetSavePoint(pane, savepoint);
+
+                                       String str = CMergeApp::GetPackingErrorMessage(pane, m_pImgMergeWindow->GetPaneCount(), m_filePaths[pane], m_infoUnpacker.m_PluginName);
+                                       int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
+                                       if (answer == IDOK)
+                                               return DoFileSaveAs(pane, false);
+                                       return false;
+                               }
+                       }
                }
                UpdateDiffItem(m_pDirDoc);
                m_fileInfo[pane].Update(m_filePaths[pane]);
@@ -706,7 +711,7 @@ bool CImgMergeFrame::DoFileSave(int pane)
        return true;
 }
 
-bool CImgMergeFrame::DoFileSaveAs(int pane)
+bool CImgMergeFrame::DoFileSaveAs(int pane, bool packing)
 {
        const String &path = m_filePaths.GetPath(pane);
        String strPath;
@@ -721,6 +726,13 @@ RETRY:
        if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
        {
                std::wstring filename = ucr::toUTF16(strPath);
+               if (packing && !m_infoUnpacker.m_PluginName.empty())
+               {
+                       String tempPath = env::GetTemporaryPath();
+                       filename = ucr::toUTF16(env::GetTemporaryFileName(tempPath, _T("MRG_"), 0)
+                               + paths::FindExtension(m_pImgMergeWindow->GetFileName(pane)));
+               }
+               int savepoint = m_pImgMergeWindow->GetSavePoint(pane);
                if (!m_pImgMergeWindow->SaveImageAs(pane, filename.c_str()))
                {
                        String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t- use a different filename (Press OK)\n\t- abort the current operation (Press Cancel)?"), strPath, GetSysError());
@@ -729,6 +741,20 @@ RETRY:
                                goto RETRY;
                        return false;
                }
+               if (filename != strPath)
+               {
+                       if (!FileTransform::Packing(filename, strPath, m_infoUnpacker, m_unpackerSubcode[pane]))
+                       {
+                               // Restore save point
+                               m_pImgMergeWindow->SetSavePoint(pane, savepoint);
+
+                               String str = CMergeApp::GetPackingErrorMessage(pane, m_pImgMergeWindow->GetPaneCount(), strPath, m_infoUnpacker.m_PluginName);
+                               int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
+                               if (answer == IDOK)
+                                       return DoFileSaveAs(pane, false);
+                               return false;
+                       }
+               }
                if (path.empty())
                {
                        // We are saving scratchpad (unnamed file)
@@ -832,7 +858,7 @@ void CImgMergeFrame::OnFileReload()
 {
        if (!PromptAndSaveIfNeeded(true))
                return;
-       m_pImgMergeWindow->ReloadImages();
+       OpenImages();
        for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
                m_fileInfo[pane].Update(m_filePaths[pane]);
 }
@@ -1032,6 +1058,24 @@ void CImgMergeFrame::UpdateSplitter()
 {
 }
 
+bool CImgMergeFrame::OpenImages()
+{
+       bool bResult;
+       String filteredFilenames = strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|"));
+       String strTempFileName[3];
+       for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
+       {
+               strTempFileName[pane] = m_filePaths[pane];
+               if (!FileTransform::Unpacking(&m_infoUnpacker, &m_unpackerSubcode[pane], strTempFileName[pane], filteredFilenames))
+                       return false;
+       }
+       if (m_filePaths.GetSize() == 2)
+               bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str());
+       else
+               bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), ucr::toUTF16(strTempFileName[2]).c_str());
+       return bResult;
+}
+
 /**
  * @brief Update associated diff item
  */
index 7718531..7487dc0 100644 (file)
@@ -19,6 +19,7 @@
 #include "LocationBar.h"
 #include "FileLocation.h"
 #include "MergeFrameCommon.h"
+#include "FileTransform.h"
 
 class CDirDoc;
 
@@ -57,6 +58,7 @@ public:
        void UpdateAutoPaneResize();
        void UpdateSplitter();
        bool GenerateReport(const String& sFileName) const override;
+       void SetUnpacker(const PackingInfo* infoUnpacker) override { if (infoUnpacker) m_infoUnpacker = *infoUnpacker; };
        void DoAutoMerge(int dstPane);
        bool IsModified() const;
        IMergeDoc::FileChange IsFileChangedOnDisk(int pane) const;
@@ -92,12 +94,13 @@ private:
        void CreateImgWndStatusBar(CStatusBar &, CWnd *);
 // Generated message map functions
 private:
+       bool OpenImages();
        int UpdateDiffItem(CDirDoc * pDirDoc);
        void UpdateHeaderSizes();
        void UpdateHeaderPath(int pane);
        void SetTitle(LPCTSTR lpszTitle);
        bool DoFileSave(int pane);
-       bool DoFileSaveAs(int pane);
+       bool DoFileSaveAs(int pane, bool packing = true);
        bool PromptAndSaveIfNeeded(bool bAllowCancel);
        bool MergeModeKeyDown(MSG* pMsg);
        static void OnChildPaneEvent(const IImgMergeWindow::Event& evt);
@@ -114,6 +117,8 @@ private:
        bool m_bAutoMerged;
        CDirDoc *m_pDirDoc;
        int m_nActivePane;
+       PackingInfo m_infoUnpacker;
+       int m_unpackerSubcode[3];
 
        //{{AFX_MSG(CImgMergeFrame)
        afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
index 8c4550a..708ddb4 100644 (file)
@@ -653,9 +653,20 @@ bool CMainFrame::ShowAutoMergeDoc(CDirDoc * pDirDoc,
        const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
        const PackingInfo * infoUnpacker /*= nullptr*/)
 {
+       ASSERT(pDirDoc != nullptr);
+
        if (sReportFile.empty() && pDirDoc->CompareFilesIfFilesAreLarge(nFiles, ifileloc))
                return false;
 
+       String unpackedFileExtension;
+       if (infoUnpacker && GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
+       {
+               std::vector<String> filepaths(nFiles);
+               std::transform(ifileloc, ifileloc + nFiles, filepaths.begin(),
+                       [](auto& file) { return file.filepath; });
+               String filteredFilenames = strutils::join(filepaths.begin(), filepaths.end(), _T("|"));
+               unpackedFileExtension = FileTransform::GetUnpackedFileExtension(filteredFilenames, infoUnpacker);
+       }
        FileFilterHelper filterImg, filterBin;
        filterImg.UseMask(true);
        filterImg.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS));
@@ -663,9 +674,10 @@ bool CMainFrame::ShowAutoMergeDoc(CDirDoc * pDirDoc,
        filterBin.SetMask(GetOptionsMgr()->GetString(OPT_CMP_BIN_FILEPATTERNS));
        for (int pane = 0; pane < nFiles; ++pane)
        {
-               if (filterImg.includeFile(ifileloc[pane].filepath) && CImgMergeFrame::IsLoadable())
+               String filepath = ifileloc[pane].filepath + unpackedFileExtension;
+               if (filterImg.includeFile(filepath) && CImgMergeFrame::IsLoadable())
                        return ShowImgMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
-               else if (filterBin.includeFile(ifileloc[pane].filepath) && CHexMergeView::IsLoadable())
+               else if (filterBin.includeFile(filepath) && CHexMergeView::IsLoadable())
                        return ShowHexMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
        }
        return ShowTextOrTableMergeDoc({}, pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
@@ -829,6 +841,8 @@ bool CMainFrame::ShowHexMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocati
        if (pHexMergeDoc == nullptr)
                return false;
 
+       pHexMergeDoc->SetUnpacker(infoUnpacker);
+
        if (!pHexMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc))
                return false;
 
@@ -848,7 +862,7 @@ bool CMainFrame::ShowImgMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocati
        if (!CImgMergeFrame::menu.m_hMenu)
                CImgMergeFrame::menu.m_hMenu = NewImgMergeViewMenu();
        pImgMergeFrame->SetSharedMenu(CImgMergeFrame::menu.m_hMenu);
-
+       pImgMergeFrame->SetUnpacker(infoUnpacker);
        pImgMergeFrame->SetDirDoc(pDirDoc);
        pDirDoc->AddMergeDoc(pImgMergeFrame);
                
index 88eed47..e05fa33 100644 (file)
@@ -1087,6 +1087,17 @@ int CMergeApp::HandleReadonlySave(String& strSavePath, bool bMultiFile,
        return nRetVal;
 }
 
+String CMergeApp::GetPackingErrorMessage(int pane, int paneCount, const String& path, const String& pluginName)
+{
+       return strutils::format_string2(
+               pane == 0 ? 
+                       _("Plugin '%2' cannot pack your changes to the left file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?")
+                       : (pane == paneCount - 1) ? 
+                               _("Plugin '%2' cannot pack your changes to the right file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?")
+                               : _("Plugin '%2' cannot pack your changes to the middle file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?"),
+               path, pluginName);
+}
+
 /**
  * @brief Is specified file a project file?
  * @param [in] filepath Full path to file to check.
index dd987cc..40bd0d5 100644 (file)
@@ -90,6 +90,7 @@ public:
        static void OpenFileToExternalEditor(const String& file, int nLineNumber = 1);
        static bool CreateBackup(bool bFolder, const String& pszPath);
        static int HandleReadonlySave(String& strSavePath, bool bMultiFile, bool &bApplyToAll);
+       static String GetPackingErrorMessage(int pane, int paneCount, const String& path, const String& pluginName);
        bool GetMergingMode() const;
        void SetMergingMode(bool bMergingMode);
        static void SetupTempPath();
index 6e61af2..0a9b4df 100644 (file)
@@ -1546,22 +1546,7 @@ bool CMergeDoc::TrySaveAs(String &strPath, int &nSaveResult, String & sError,
        // Select message based on reason function called
        if (nSaveResult == SAVE_PACK_FAILED)
        {
-               if (m_nBuffers == 3)
-               {
-                       str = strutils::format_string2(
-                               nBuffer == 0 ? 
-                                       _("Plugin '%2' cannot pack your changes to the left file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?")
-                                       : (nBuffer == 1 ? 
-                                       _("Plugin '%2' cannot pack your changes to the middle file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?"): 
-                                       _("Plugin '%2' cannot pack your changes to the right file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?")),
-                               strPath, pInfoTempUnpacker->m_PluginName);
-               }
-               else
-               {
-                       str = strutils::format_string2(nBuffer == 0 ? _("Plugin '%2' cannot pack your changes to the left file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?") : 
-                               _("Plugin '%2' cannot pack your changes to the right file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?"),
-                               strPath, pInfoTempUnpacker->m_PluginName);
-               }
+               str = CMergeApp::GetPackingErrorMessage(nBuffer, m_nBuffers, strPath, pInfoTempUnpacker->m_PluginName);
                // replace the unpacker with a "do nothing" unpacker
                pInfoTempUnpacker->Initialize(PLUGIN_MODE::PLUGIN_MANUAL);
        }