OSDN Git Service

9d54ea9a0d71d2ee686a6d577a8189865b7404e3
[winmerge-jp/winmerge-jp.git] / Src / FolderCmp.cpp
1 /** 
2  * @file  FolderCmp.cpp
3  *
4  * @brief Implementation file for FolderCmp
5  */
6
7 #include "pch.h"
8 #include "diff.h"
9 #include "FolderCmp.h"
10 #include <cassert>
11 #include "Wrap_DiffUtils.h"
12 #include "ByteCompare.h"
13 #include "paths.h"
14 #include "FilterList.h"
15 #include "DiffContext.h"
16 #include "DiffList.h"
17 #include "DiffWrapper.h"
18 #include "FileTransform.h"
19 #include "codepage_detect.h"
20 #include "BinaryCompare.h"
21 #include "TimeSizeCompare.h"
22 #include "TFile.h"
23 #include "FileFilterHelper.h"
24 #include "DebugNew.h"
25
26 using CompareEngines::ByteCompare;
27 using CompareEngines::BinaryCompare;
28 using CompareEngines::TimeSizeCompare;
29 using CompareEngines::ImageCompare;
30
31 static void GetComparePaths(CDiffContext * pCtxt, const DIFFITEM &di, PathContext & files);
32
33 FolderCmp::FolderCmp(CDiffContext *pCtxt)
34 : m_pCtxt(pCtxt)
35 , m_pDiffUtilsEngine(nullptr)
36 , m_pByteCompare(nullptr)
37 , m_pBinaryCompare(nullptr)
38 , m_pTimeSizeCompare(nullptr)
39 , m_ndiffs(CDiffContext::DIFFS_UNKNOWN)
40 , m_ntrivialdiffs(CDiffContext::DIFFS_UNKNOWN)
41 {
42 }
43
44 FolderCmp::~FolderCmp()
45 {
46 }
47
48 bool FolderCmp::RunPlugins(PluginsContext * plugCtxt, String &errStr)
49 {
50         // FIXME:
51         return true;
52 }
53
54 void FolderCmp::CleanupAfterPlugins(PluginsContext *plugCtxt)
55 {
56 }
57
58 /**
59  * @brief Prepare files (run plugins) & compare them, and return diffcode.
60  * This is function to compare two files in folder compare. It is not used in
61  * file compare.
62  * @param [in] pCtxt Pointer to compare context.
63  * @param [in, out] di Compared files with associated data.
64  * @return Compare result code.
65  */
66 int FolderCmp::prepAndCompareFiles(DIFFITEM &di)
67 {
68         int nIndex;
69         int nCompMethod = m_pCtxt->GetCompareMethod();
70         int nDirs = m_pCtxt->GetCompareDirs();
71
72         unsigned code = DIFFCODE::FILE | DIFFCODE::CMPERR;
73
74         if (nCompMethod == CMP_CONTENT || nCompMethod == CMP_QUICK_CONTENT)
75         {
76                 if ((di.diffFileInfo[0].size > m_pCtxt->m_nBinaryCompareLimit && di.diffFileInfo[0].size != DirItem::FILE_SIZE_NONE) ||
77                         (di.diffFileInfo[1].size > m_pCtxt->m_nBinaryCompareLimit && di.diffFileInfo[1].size != DirItem::FILE_SIZE_NONE) ||
78                         (nDirs > 2 && di.diffFileInfo[2].size > m_pCtxt->m_nBinaryCompareLimit && di.diffFileInfo[2].size != DirItem::FILE_SIZE_NONE))
79                 {
80                         nCompMethod = CMP_BINARY_CONTENT;
81                 }
82                 else if (m_pCtxt->m_bEnableImageCompare && (
83                         di.diffFileInfo[0].size != DirItem::FILE_SIZE_NONE && m_pCtxt->m_pImgfileFilter->includeFile(di.diffFileInfo[0].filename) ||
84                         di.diffFileInfo[1].size != DirItem::FILE_SIZE_NONE && m_pCtxt->m_pImgfileFilter->includeFile(di.diffFileInfo[1].filename) ||
85                         nDirs > 2 && di.diffFileInfo[2].size != DirItem::FILE_SIZE_NONE && m_pCtxt->m_pImgfileFilter->includeFile(di.diffFileInfo[2].filename)))
86                 {
87                         nCompMethod = CMP_IMAGE_CONTENT;
88                 }
89         }
90
91         if (nCompMethod == CMP_CONTENT ||
92                 nCompMethod == CMP_QUICK_CONTENT)
93         {
94
95                 // Reset text stats
96                 for (nIndex = 0; nIndex < nDirs; nIndex++)
97                         m_diffFileData.m_textStats[nIndex].clear();
98
99                 PathContext tFiles;
100                 GetComparePaths(m_pCtxt, di, tFiles);
101                 struct change *script10 = nullptr;
102                 struct change *script12 = nullptr;
103                 struct change *script02 = nullptr;
104                 DiffFileData diffdata10, diffdata12, diffdata02;
105                 String filepathUnpacked[3];
106                 String filepathTransformed[3];
107                 int codepage = 0;
108
109                 // For user chosen plugins, define bAutomaticUnpacker as false and use the chosen infoHandler
110                 // but how can we receive the infoHandler ? DirScan actually only 
111                 // returns info, but can not use file dependent information.
112
113                 // Transformation happens here
114                 // text used for automatic mode : plugin filter must match it
115                 String filteredFilenames = strutils::join(tFiles.begin(), tFiles.end(), _T("|"));
116
117                 PackingInfo * infoUnpacker = nullptr;
118                 PrediffingInfo * infoPrediffer = nullptr;
119
120                 // Get existing or new plugin infos
121                 if (m_pCtxt->m_piPluginInfos != nullptr)
122                         m_pCtxt->FetchPluginInfos(filteredFilenames, &infoUnpacker,
123                                         &infoPrediffer);
124
125                 FileTextEncoding encoding[3];
126                 bool bForceUTF8 = m_pCtxt->GetCompareOptions(nCompMethod)->m_bIgnoreCase;
127
128                 for (nIndex = 0; nIndex < nDirs; nIndex++)
129                 {
130                 // plugin may alter filepaths to temp copies (which we delete before returning in all cases)
131                         filepathUnpacked[nIndex] = tFiles[nIndex];
132
133                         //DiffFileData diffdata; //(filepathTransformed1, filepathTransformed2);
134                         // Invoke unpacking plugins
135                         if (infoUnpacker && strutils::compare_nocase(filepathUnpacked[nIndex], _T("NUL")) != 0)
136                         {
137                                 if (!FileTransform::Unpacking(infoUnpacker, filepathUnpacked[nIndex], filteredFilenames))
138                                         goto exitPrepAndCompare;
139
140                                 // we use the same plugins for both files, so they must be defined before second file
141                                 assert(infoUnpacker->m_PluginOrPredifferMode == PLUGIN_MODE::PLUGIN_MANUAL);
142                         }
143
144                         // As we keep handles open on unpacked files, Transform() may not delete them.
145                         // Unpacked files will be deleted at end of this function.
146                         filepathTransformed[nIndex] = filepathUnpacked[nIndex];
147
148                         encoding[nIndex] = codepage_detect::Guess(filepathTransformed[nIndex], m_pCtxt->m_iGuessEncodingType);
149                         m_diffFileData.m_FileLocation[nIndex].encoding = encoding[nIndex];
150                 }
151
152                 if (!std::equal(encoding + 1, encoding + nDirs, encoding))
153                         bForceUTF8 = true;
154                 codepage = bForceUTF8 ? CP_UTF8 : (encoding[0].m_unicoding ? CP_UTF8 : encoding[0].m_codepage);
155                 for (nIndex = 0; nIndex < nDirs; nIndex++)
156                 {
157                 // Invoke prediff'ing plugins
158                         if (infoPrediffer && !m_diffFileData.Filepath_Transform(bForceUTF8, encoding[nIndex], filepathUnpacked[nIndex], filepathTransformed[nIndex], filteredFilenames, infoPrediffer))
159                                 goto exitPrepAndCompare;
160                 }
161
162                 // If options are binary equivalent, we could check for filesize
163                 // difference here, and bail out if files are clearly different
164                 // But, then we don't know if file is ascii or binary, and this
165                 // affects behavior (also, we don't have an icon for unknown type)
166
167                 // Actually compare the files
168                 // `diffutils_compare_files()` is a fairly thin front-end to GNU diffutils
169
170                 if (tFiles.GetSize() == 2)
171                 {
172                         m_diffFileData.SetDisplayFilepaths(tFiles[0], tFiles[1]); // store true names for diff utils patch file
173                         // This opens & fstats both files (if it succeeds)
174                         if (!m_diffFileData.OpenFiles(filepathTransformed[0], filepathTransformed[1]))
175                                 goto exitPrepAndCompare;
176                 }
177                 else
178                 {
179                         diffdata10.SetDisplayFilepaths(tFiles[1], tFiles[0]); // store true names for diff utils patch file
180                         diffdata12.SetDisplayFilepaths(tFiles[1], tFiles[2]); // store true names for diff utils patch file
181                         diffdata02.SetDisplayFilepaths(tFiles[0], tFiles[2]); // store true names for diff utils patch file
182
183                         if (!diffdata10.OpenFiles(filepathTransformed[1], filepathTransformed[0]))
184                                 goto exitPrepAndCompare;
185
186                         if (!diffdata12.OpenFiles(filepathTransformed[1], filepathTransformed[2]))
187                                 goto exitPrepAndCompare;
188
189                         if (!diffdata02.OpenFiles(filepathTransformed[0], filepathTransformed[2]))
190                                 goto exitPrepAndCompare;
191                 }
192
193                 // If either file is larger than limit compare files by quick contents
194                 // This allows us to (faster) compare big binary files
195                 if (nCompMethod == CMP_CONTENT && 
196                         (di.diffFileInfo[0].size > m_pCtxt->m_nQuickCompareLimit ||
197                         di.diffFileInfo[1].size > m_pCtxt->m_nQuickCompareLimit ||
198                         (nDirs > 2 && di.diffFileInfo[2].size > m_pCtxt->m_nQuickCompareLimit)))
199                 {
200                         nCompMethod = CMP_QUICK_CONTENT;
201                 }
202
203                 if (nCompMethod == CMP_CONTENT)
204                 {
205                         if (m_pDiffUtilsEngine == nullptr)
206                         {
207                                 m_pDiffUtilsEngine.reset(new CompareEngines::DiffUtils());
208                                 m_pDiffUtilsEngine->SetCodepage(codepage);
209                                 m_pDiffUtilsEngine->SetCompareOptions(*m_pCtxt->GetCompareOptions(CMP_CONTENT));
210                                 if (m_pCtxt->m_pFilterList != nullptr)
211                                         m_pDiffUtilsEngine->SetFilterList(m_pCtxt->m_pFilterList.get());
212                                 else
213                                         m_pDiffUtilsEngine->ClearFilterList();
214                                 if (m_pCtxt->m_pSubstitutionList != nullptr)
215                                         m_pDiffUtilsEngine->SetSubstitutionList(m_pCtxt->m_pSubstitutionList);
216                                 else
217                                         m_pDiffUtilsEngine->ClearSubstitutionList();
218                         }
219                         if (tFiles.GetSize() == 2)
220                         {
221                                 m_pDiffUtilsEngine->SetFileData(2, m_diffFileData.m_inf);
222                                 code = m_pDiffUtilsEngine->diffutils_compare_files();
223                                 m_pDiffUtilsEngine->GetDiffCounts(m_ndiffs, m_ntrivialdiffs);
224                                 m_pDiffUtilsEngine->GetTextStats(0, &m_diffFileData.m_textStats[0]);
225                                 m_pDiffUtilsEngine->GetTextStats(1, &m_diffFileData.m_textStats[1]);
226
227                                 // If unique item, it was being compared to itself to determine encoding
228                                 // and the #diffs is invalid
229                                 if (di.diffcode.isSideSecondOnly() || di.diffcode.isSideFirstOnly())
230                                 {
231                                         m_ndiffs = CDiffContext::DIFFS_UNKNOWN;
232                                         m_ntrivialdiffs = CDiffContext::DIFFS_UNKNOWN;
233                                 }
234                         }
235                         else
236                         {
237                                 bool bRet;
238                                 int bin_flag10 = 0, bin_flag12 = 0, bin_flag02 = 0;
239
240                                 m_pDiffUtilsEngine->SetFileData(2, diffdata10.m_inf);
241                                 bRet = m_pDiffUtilsEngine->Diff2Files(&script10, 0, &bin_flag10, false, nullptr);
242                                 m_pDiffUtilsEngine->GetTextStats(0, &m_diffFileData.m_textStats[1]);
243                                 m_pDiffUtilsEngine->GetTextStats(1, &m_diffFileData.m_textStats[0]);
244
245                                 m_pDiffUtilsEngine->SetFileData(2, diffdata12.m_inf);
246                                 bRet = m_pDiffUtilsEngine->Diff2Files(&script12, 0, &bin_flag12, false, nullptr);
247                                 m_pDiffUtilsEngine->GetTextStats(0, &m_diffFileData.m_textStats[1]);
248                                 m_pDiffUtilsEngine->GetTextStats(1, &m_diffFileData.m_textStats[2]);
249
250                                 m_pDiffUtilsEngine->SetFileData(2, diffdata02.m_inf);
251                                 bRet = m_pDiffUtilsEngine->Diff2Files(&script02, 0, &bin_flag02, false, nullptr);
252                                 m_pDiffUtilsEngine->GetTextStats(0, &m_diffFileData.m_textStats[0]);
253                                 m_pDiffUtilsEngine->GetTextStats(1, &m_diffFileData.m_textStats[2]);
254
255                                 code = DIFFCODE::FILE;
256
257                                 String Ext = tFiles[0];
258                                 size_t PosOfDot = Ext.rfind('.');
259                                 if (PosOfDot != String::npos)
260                                         Ext.erase(0, PosOfDot + 1);
261
262                                 CDiffWrapper dw;
263                                 DiffList diffList;
264                                 DIFFSTATUS status;
265
266                                 diffList.Clear();
267                                 dw.SetCompareFiles(tFiles);
268                                 dw.SetOptions(m_pCtxt->GetOptions());
269                                 dw.SetFilterList(m_pCtxt->m_pFilterList.get());
270                                 dw.SetSubstitutionList(m_pCtxt->m_pSubstitutionList);
271                                 dw.SetFilterCommentsSourceDef(Ext);
272                                 dw.SetCreateDiffList(&diffList);
273                                 dw.LoadWinMergeDiffsFromDiffUtilsScript3(
274                                         script10, script12,
275                                         diffdata10.m_inf, diffdata12.m_inf);
276                                 m_ndiffs = diffList.GetSignificantDiffs(); 
277                                 m_ntrivialdiffs = diffList.GetSize() - m_ndiffs;
278                                 
279                                 if (m_ndiffs > 0 || bin_flag10 < 0 || bin_flag12 < 0)
280                                         code |= DIFFCODE::DIFF;
281                                 else
282                                         code |= DIFFCODE::SAME;
283                                 if (bin_flag10 || bin_flag12)
284                                         code |= DIFFCODE::BIN;
285                                 else
286                                         code |= DIFFCODE::TEXT;
287
288                                 if ((code & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF)
289                                 {
290                                         if ((code & DIFFCODE::TEXTFLAGS) == DIFFCODE::TEXT)
291                                         {
292                                                 if (script12 == nullptr)
293                                                         code |= DIFFCODE::DIFF1STONLY;
294                                                 else if (script02 == nullptr)
295                                                         code |= DIFFCODE::DIFF2NDONLY;
296                                                 else if (script10 == nullptr)
297                                                         code |= DIFFCODE::DIFF3RDONLY;
298                                         }
299                                         else
300                                         {
301                                                 if (bin_flag12 > 0)
302                                                         code |= DIFFCODE::DIFF1STONLY;
303                                                 else if (bin_flag02 > 0)
304                                                         code |= DIFFCODE::DIFF2NDONLY;
305                                                 else if (bin_flag10 > 0)
306                                                         code |= DIFFCODE::DIFF3RDONLY;
307                                         }
308                                 }
309
310                                 // If unique item, it was being compared to itself to determine encoding
311                                 // and the #diffs is invalid
312                                 if (di.diffcode.isSideFirstOnly() || di.diffcode.isSideSecondOnly() || di.diffcode.isSideThirdOnly())
313                                 {
314                                         m_ndiffs = CDiffContext::DIFFS_UNKNOWN;
315                                         m_ntrivialdiffs = CDiffContext::DIFFS_UNKNOWN;
316                                 }
317
318                                 dw.FreeDiffUtilsScript(script10);
319                                 dw.FreeDiffUtilsScript(script12);
320                                 dw.FreeDiffUtilsScript(script02);
321                         }
322
323                 }
324                 else if (nCompMethod == CMP_QUICK_CONTENT)
325                 {
326                         // use our own byte-by-byte compare
327                         if (m_pByteCompare == nullptr)
328                         {
329                                 m_pByteCompare.reset(new ByteCompare());
330                                 m_pByteCompare->SetCompareOptions(*m_pCtxt->GetCompareOptions(CMP_QUICK_CONTENT));
331
332                                 m_pByteCompare->SetAdditionalOptions(m_pCtxt->m_bStopAfterFirstDiff);
333                                 m_pByteCompare->SetAbortable(m_pCtxt->GetAbortable());
334                         }
335                         if (tFiles.GetSize() == 2)
336                         {
337                                 m_pByteCompare->SetFileData(2, m_diffFileData.m_inf);
338
339                                 // use our own byte-by-byte compare
340                                 code = m_pByteCompare->CompareFiles(m_diffFileData.m_FileLocation);
341
342                                 m_pByteCompare->GetTextStats(0, &m_diffFileData.m_textStats[0]);
343                                 m_pByteCompare->GetTextStats(1, &m_diffFileData.m_textStats[1]);
344
345                                 // Quick contents doesn't know about diff counts
346                                 // Set to special value to indicate invalid
347                                 m_ndiffs = CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE;
348                                 m_ntrivialdiffs = CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE;
349                         }
350                         else
351                         {
352                                 // 10
353                                 m_pByteCompare->SetFileData(2, diffdata10.m_inf);
354
355                                 // use our own byte-by-byte compare
356                                 int code10 = m_pByteCompare->CompareFiles(diffdata10.m_FileLocation);
357
358                                 m_pByteCompare->GetTextStats(0, &m_diffFileData.m_textStats[1]);
359                                 m_pByteCompare->GetTextStats(1, &m_diffFileData.m_textStats[0]);
360
361                                 // 12
362                                 m_pByteCompare->SetFileData(2, diffdata12.m_inf);
363
364                                 // use our own byte-by-byte compare
365                                 int code12 = m_pByteCompare->CompareFiles(diffdata12.m_FileLocation);
366
367                                 m_pByteCompare->GetTextStats(0, &m_diffFileData.m_textStats[1]);
368                                 m_pByteCompare->GetTextStats(1, &m_diffFileData.m_textStats[2]);
369
370                                 // 02
371                                 m_pByteCompare->SetFileData(2, diffdata02.m_inf);
372
373                                 // use our own byte-by-byte compare
374                                 int code02 = m_pByteCompare->CompareFiles(diffdata02.m_FileLocation);
375
376                                 m_pByteCompare->GetTextStats(0, &m_diffFileData.m_textStats[0]);
377                                 m_pByteCompare->GetTextStats(1, &m_diffFileData.m_textStats[2]);
378
379                                 code = DIFFCODE::FILE;
380                                 if (DIFFCODE::isResultError(code10) || DIFFCODE::isResultError(code12) || DIFFCODE::isResultError(code02))
381                                         code |= DIFFCODE::CMPERR;
382                                 if ((code10 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF || (code12 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF)
383                                         code |= DIFFCODE::DIFF;
384                                 else
385                                         code |= DIFFCODE::SAME;
386                                 if ((code10 & DIFFCODE::TEXTFLAGS) == DIFFCODE::TEXT && 
387                                         (code12 & DIFFCODE::TEXTFLAGS) == DIFFCODE::TEXT)
388                                         code |= DIFFCODE::TEXT;
389                                 else
390                                         code |= DIFFCODE::BIN;
391                                 if ((code10 & DIFFCODE::TEXTFLAGS) == (DIFFCODE::BIN | DIFFCODE::BINSIDE1))
392                                         code |= DIFFCODE::BINSIDE2;
393                                 if ((code10 & DIFFCODE::TEXTFLAGS) == (DIFFCODE::BIN | DIFFCODE::BINSIDE2))
394                                         code |= DIFFCODE::BINSIDE1;
395                                 if ((code12 & DIFFCODE::TEXTFLAGS) == (DIFFCODE::BIN | DIFFCODE::BINSIDE2))
396                                         code |= DIFFCODE::BINSIDE3;
397                                 if ((code & DIFFCODE::COMPAREFLAGS) == DIFFCODE::DIFF)
398                                 {
399                                         if ((code12 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME)
400                                                 code |= DIFFCODE::DIFF1STONLY;
401                                         else if ((code02 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME)
402                                                 code |= DIFFCODE::DIFF2NDONLY;
403                                         else if ((code10 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME)
404                                                 code |= DIFFCODE::DIFF3RDONLY;
405                                 }
406
407                                 // Quick contents doesn't know about diff counts
408                                 // Set to special value to indicate invalid
409                                 m_ndiffs = CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE;
410                                 m_ntrivialdiffs = CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE;
411                         }
412                 }
413 exitPrepAndCompare:
414                 m_diffFileData.Reset();
415                 diffdata10.Reset();
416                 diffdata12.Reset();
417                 diffdata02.Reset();
418                 
419                 // delete the temp files after comparison
420                 if (filepathTransformed[0] != filepathUnpacked[0])
421                         try { TFile(filepathTransformed[0]).remove(); } catch (...) { LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), filepathTransformed[0])); }
422                 if (filepathTransformed[1] != filepathUnpacked[1])
423                         try { TFile(filepathTransformed[1]).remove(); } catch (...) { LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), filepathTransformed[1])); }
424                 if (nDirs > 2 && filepathTransformed[2] != filepathUnpacked[2])
425                         try { TFile(filepathTransformed[2]).remove(); } catch (...) { LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), filepathTransformed[2])); }
426                 if (filepathUnpacked[0] != tFiles[0])
427                         try { TFile(filepathUnpacked[0]).remove(); } catch (...) { LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), filepathUnpacked[0])); }
428                 if (filepathUnpacked[1] != tFiles[1])
429                         try { TFile(filepathUnpacked[1]).remove(); } catch (...) { LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), filepathUnpacked[1])); }
430                 if (nDirs > 2 && filepathUnpacked[2] != tFiles[2])
431                         try { TFile(filepathUnpacked[2]).remove(); } catch (...) { LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), filepathUnpacked[2])); }
432
433                 // When comparing empty file and nonexistent file, `DIFFCODE::SAME` flag is set to the variable `code`, so change the flag to `DIFFCODE::DIFF`
434                 // Also when disabling ignore codepage option and the encodings of files are not equal, change the flag to `DIFFCODE::DIFF even if  `DIFFCODE::SAME` flag is set to the variable `code`
435                 if (!di.diffcode.existAll() || (!m_pCtxt->m_bIgnoreCodepage && !std::equal(encoding + 1, encoding + nDirs, encoding)))
436                         code = (code & ~DIFFCODE::COMPAREFLAGS) | DIFFCODE::DIFF;
437         }
438         else if (nCompMethod == CMP_BINARY_CONTENT)
439         {
440                 if (m_pBinaryCompare == nullptr)
441                         m_pBinaryCompare.reset(new BinaryCompare());
442                 m_pBinaryCompare->SetAbortable(m_pCtxt->GetAbortable());
443                 PathContext tFiles;
444                 GetComparePaths(m_pCtxt, di, tFiles);
445                 code = m_pBinaryCompare->CompareFiles(tFiles, di);
446         }
447         else if (nCompMethod == CMP_DATE || nCompMethod == CMP_DATE_SIZE || nCompMethod == CMP_SIZE)
448         {
449                 if (m_pTimeSizeCompare == nullptr)
450                         m_pTimeSizeCompare.reset(new TimeSizeCompare());
451
452                 m_pTimeSizeCompare->SetAdditionalOptions(m_pCtxt->m_bIgnoreSmallTimeDiff);
453                 code = m_pTimeSizeCompare->CompareFiles(nCompMethod, m_pCtxt->GetCompareDirs(), di);
454         }
455         else if (nCompMethod == CMP_IMAGE_CONTENT)
456         {
457                 if (!m_pImageCompare)
458                 {
459                         m_pImageCompare.reset(new ImageCompare());
460                         m_pImageCompare->SetColorDistanceThreshold(m_pCtxt->m_dColorDistanceThreshold);
461                 }
462
463                 PathContext tFiles;
464                 GetComparePaths(m_pCtxt, di, tFiles);
465                 code = DIFFCODE::IMAGE | m_pImageCompare->CompareFiles(tFiles, di);
466         }
467         else
468         {
469                 // Print error since we should have handled by date compare earlier
470                 throw "Invalid compare type, DiffFileData can't handle it";
471         }
472
473         return code;
474 }
475
476 /**
477  * @brief Get actual compared paths from DIFFITEM.
478  * @param [in] pCtx Pointer to compare context.
479  * @param [in] di DiffItem from which the paths are created.
480  * @param [out] left Gets the left compare path.
481  * @param [out] right Gets the right compare path.
482  * @note If item is unique, same path is returned for both.
483  */
484 void GetComparePaths(CDiffContext * pCtxt, const DIFFITEM &di, PathContext & tFiles)
485 {
486         int nDirs = pCtxt->GetCompareDirs();
487
488         tFiles.SetSize(nDirs);
489
490         for (int nIndex = 0; nIndex < nDirs; nIndex++)
491         {
492                 if (di.diffcode.exists(nIndex))
493                 {
494                         tFiles.SetPath(nIndex,
495                                 paths::ConcatPath(pCtxt->GetPath(nIndex), di.diffFileInfo[nIndex].GetFile()), false);
496                 }
497                 else
498                 {
499                         tFiles.SetPath(nIndex, _T("NUL"), false);
500                 }
501         }
502 }