480 lines
22 KiB
C#
480 lines
22 KiB
C#
/******************************************************
|
|
* ROMVault2 is written by Gordon J. *
|
|
* Contact gordon@romvault.com *
|
|
* Copyright 2014 *
|
|
******************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Threading;
|
|
using ROMVault2.Properties;
|
|
using ROMVault2.RvDB;
|
|
using ROMVault2.Utils;
|
|
|
|
namespace ROMVault2
|
|
{
|
|
|
|
public static class FindFixes
|
|
{
|
|
private static BackgroundWorker _bgw;
|
|
|
|
public static void ScanFiles(object sender, DoWorkEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
_bgw = sender as BackgroundWorker;
|
|
if (_bgw == null) return;
|
|
|
|
Program.SyncCont = e.Argument as SynchronizationContext;
|
|
if (Program.SyncCont == null)
|
|
{
|
|
_bgw = null;
|
|
return;
|
|
}
|
|
|
|
_bgw.ReportProgress(0, new bgwText("Clearing DB Status"));
|
|
RepairStatus.ReportStatusReset(DB.DirTree);
|
|
|
|
List<RvFile> lstRomTableSortedCRCSize;
|
|
List<RvFile> lstRomTableSortedSHA1CHD;
|
|
|
|
_bgw.ReportProgress(0, new bgwText("Loading Rom List"));
|
|
DBHelper.GetSelectedFilesSortCRCSize(out lstRomTableSortedCRCSize);
|
|
DBHelper.GetSelectedFilesSortSHA1CHD(out lstRomTableSortedSHA1CHD);
|
|
|
|
_bgw.ReportProgress(0, new bgwText("Scanning for Fixes"));
|
|
_bgw.ReportProgress(0, new bgwSetRange(lstRomTableSortedCRCSize.Count));
|
|
|
|
int romIndex0 = 0;
|
|
int romIndex1 = 1;
|
|
while (romIndex1 < lstRomTableSortedCRCSize.Count)
|
|
{
|
|
if (romIndex1 % 100 == 0) _bgw.ReportProgress(romIndex1);
|
|
|
|
if (!ArrByte.bCompare(lstRomTableSortedCRCSize[romIndex0].CRC,lstRomTableSortedCRCSize[romIndex1].CRC) || lstRomTableSortedCRCSize[romIndex0].Size != lstRomTableSortedCRCSize[romIndex1].Size)
|
|
{
|
|
ListCheck(lstRomTableSortedCRCSize, romIndex0, romIndex1 - romIndex0);
|
|
romIndex0 = romIndex1;
|
|
}
|
|
romIndex1++;
|
|
}
|
|
|
|
ListCheck(lstRomTableSortedCRCSize, romIndex0, romIndex1 - romIndex0);
|
|
|
|
|
|
|
|
_bgw.ReportProgress(0, new bgwSetRange(lstRomTableSortedSHA1CHD.Count));
|
|
|
|
romIndex0 = 0;
|
|
romIndex1 = 1;
|
|
while (romIndex1 < lstRomTableSortedSHA1CHD.Count)
|
|
{
|
|
if (romIndex1 % 100 == 0) _bgw.ReportProgress(romIndex1);
|
|
|
|
if (!ArrByte.bCompare(lstRomTableSortedSHA1CHD[romIndex0].SHA1CHD,lstRomTableSortedSHA1CHD[romIndex1].SHA1CHD) )
|
|
{
|
|
ListCheckSHA1CHD(lstRomTableSortedSHA1CHD, romIndex0, romIndex1 - romIndex0);
|
|
romIndex0 = romIndex1;
|
|
}
|
|
romIndex1++;
|
|
}
|
|
|
|
ListCheckSHA1CHD(lstRomTableSortedSHA1CHD, romIndex0, romIndex1 - romIndex0);
|
|
|
|
|
|
_bgw = null;
|
|
Program.SyncCont = null;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
ReportError.UnhandledExceptionHandler(exc);
|
|
|
|
if (_bgw != null) _bgw.ReportProgress(0, new bgwText("Updating Cache"));
|
|
DB.Write();
|
|
if (_bgw != null) _bgw.ReportProgress(0, new bgwText("Complete"));
|
|
|
|
_bgw = null;
|
|
Program.SyncCont = null;
|
|
}
|
|
}
|
|
|
|
public static void ListCheck(List<RvFile> lstRomTableSortedCRC, int start, int length)
|
|
{
|
|
if (lstRomTableSortedCRC.Count == 0)
|
|
return;
|
|
|
|
List<RvFile> missingFiles = new List<RvFile>(); // files we dont have that we need
|
|
|
|
List<RvFile> correctFiles = new List<RvFile>(); // files we have that are in the correct place
|
|
List<RvFile> unNeededFiles = new List<RvFile>(); // files we have that are not in the correct place
|
|
List<RvFile> inToSortFiles = new List<RvFile>(); // files we have that are in tosort
|
|
List<RvFile> allGotFiles = new List<RvFile>(); // all files we have
|
|
|
|
List<RvFile> corruptFiles = new List<RvFile>(); // corrupt files that we do not need, a corrupt file is missing if it is needed
|
|
|
|
|
|
// set the found status of this file
|
|
for (int iLoop = 0; iLoop < length; iLoop++)
|
|
{
|
|
RvFile tFile = lstRomTableSortedCRC[start + iLoop];
|
|
|
|
switch (tFile.RepStatus)
|
|
{
|
|
case RepStatus.UnScanned:
|
|
break;
|
|
case RepStatus.Missing:
|
|
missingFiles.Add(tFile); // these are checked in step 1 to fixes from the allGotFiles List.
|
|
break;
|
|
case RepStatus.Correct:
|
|
correctFiles.Add(tFile);
|
|
break;
|
|
case RepStatus.Corrupt:
|
|
if (tFile.DatStatus == DatStatus.InDatCollect)
|
|
missingFiles.Add(tFile); // corrupt files that are also InDatcollect are treated as missing files, and a fix should be found.
|
|
else
|
|
corruptFiles.Add(tFile); // all other corrupt files should be deleted or moved to tosort/corrupt
|
|
break;
|
|
case RepStatus.UnNeeded:
|
|
case RepStatus.Unknown:
|
|
unNeededFiles.Add(tFile);
|
|
break;
|
|
case RepStatus.NotCollected:
|
|
break;
|
|
case RepStatus.InToSort:
|
|
inToSortFiles.Add(tFile);
|
|
break;
|
|
case RepStatus.Ignore:
|
|
break; // Ignore File
|
|
default:
|
|
ReportError.SendAndShow(Resources.FindFixes_ListCheck_Unknown_test_status + tFile.DatFullName + Resources.Comma + tFile.DatStatus + Resources.Comma + tFile.RepStatus);
|
|
break;
|
|
|
|
}
|
|
}
|
|
allGotFiles.AddRange(correctFiles);
|
|
allGotFiles.AddRange(unNeededFiles);
|
|
allGotFiles.AddRange(inToSortFiles);
|
|
|
|
#region Step 1 Check the Missing files from the allGotFiles List.
|
|
// check to see if we can find any of the missing files in the gotFiles list.
|
|
// if we find them mark them as CanBeFixed,
|
|
// or if they are missing corrupt files set then as corruptCanBefixed
|
|
|
|
foreach (RvFile missingFile in missingFiles)
|
|
{
|
|
if (DBHelper.IsZeroLengthFile(missingFile))
|
|
{
|
|
missingFile.RepStatus = missingFile.RepStatus == RepStatus.Corrupt ? RepStatus.CorruptCanBeFixed : RepStatus.CanBeFixed;
|
|
continue;
|
|
}
|
|
|
|
foreach (RvFile gotFile in allGotFiles)
|
|
{
|
|
if (!CheckIfMissingFileCanBeFixedByGotFile(missingFile, gotFile)) continue;
|
|
missingFile.RepStatus = missingFile.RepStatus == RepStatus.Corrupt ? RepStatus.CorruptCanBeFixed : RepStatus.CanBeFixed;
|
|
break;
|
|
}
|
|
if (missingFile.RepStatus == RepStatus.Corrupt) missingFile.RepStatus = RepStatus.MoveToCorrupt;
|
|
}
|
|
#endregion
|
|
|
|
#region Step 2 Check all corrupt files.
|
|
// if we have a correct version of the corrupt file then the corrput file can just be deleted,
|
|
// otherwise if the corrupt file is not already in ToSort it should be moved out to ToSort.
|
|
|
|
// we can only check corrupt files using the CRC from the ZIP header, as it is corrupt so we cannot get a correct SHA1 / MD5 to check with
|
|
|
|
foreach (RvFile corruptFile in corruptFiles)
|
|
{
|
|
if (allGotFiles.Count>0)
|
|
corruptFile.RepStatus = RepStatus.Delete;
|
|
|
|
if (corruptFile.RepStatus == RepStatus.Corrupt && corruptFile.DatStatus != DatStatus.InToSort)
|
|
corruptFile.RepStatus = RepStatus.MoveToCorrupt;
|
|
}
|
|
#endregion
|
|
|
|
#region Step 3 Check if unNeeded files are needed for a fix, otherwise delete them or move them to tosort
|
|
foreach (RvFile unNeededFile in unNeededFiles)
|
|
{
|
|
/*
|
|
// check if we have a confirmed SHA1 / MD5 match of this file, and if we do we just mark this file to be deleted.
|
|
foreach (RvFile correctFile in correctFiles)
|
|
{
|
|
if (!FindSHA1MD5MatchingFiles(unNeededFile, correctFile)) continue;
|
|
unNeededFile.RepStatus = RepStatus.Delete;
|
|
break;
|
|
}
|
|
if (unNeededFile.RepStatus == RepStatus.Delete) continue;
|
|
*/
|
|
|
|
if (DBHelper.IsZeroLengthFile(unNeededFile))
|
|
{
|
|
unNeededFile.RepStatus = RepStatus.Delete;
|
|
continue;
|
|
}
|
|
|
|
// check if the unNeededFile is needed to fix a missing file
|
|
foreach (RvFile missingFile in missingFiles)
|
|
{
|
|
if (!CheckIfMissingFileCanBeFixedByGotFile(missingFile, unNeededFile)) continue;
|
|
unNeededFile.RepStatus = RepStatus.NeededForFix;
|
|
break;
|
|
}
|
|
if (unNeededFile.RepStatus == RepStatus.NeededForFix) continue;
|
|
|
|
// now that we know this file is not needed for a fix do a CRC only find against correct files to see if this file can be deleted.
|
|
foreach (RvFile correctFile in correctFiles)
|
|
{
|
|
if (!CheckIfGotfileAndMatchingFileAreFullMatches(unNeededFile, correctFile)) continue;
|
|
unNeededFile.RepStatus = RepStatus.Delete;
|
|
break;
|
|
}
|
|
if (unNeededFile.RepStatus == RepStatus.Delete) continue;
|
|
|
|
// and finally see if the file is already in ToSort, and if it is deleted.
|
|
foreach (RvFile inToSortFile in inToSortFiles)
|
|
{
|
|
if (!CheckIfGotfileAndMatchingFileAreFullMatches(unNeededFile, inToSortFile)) continue;
|
|
unNeededFile.RepStatus = RepStatus.Delete;
|
|
break;
|
|
}
|
|
if (unNeededFile.RepStatus == RepStatus.Delete) continue;
|
|
|
|
// otherwise move the file out to ToSort
|
|
unNeededFile.RepStatus = RepStatus.MoveToSort;
|
|
}
|
|
#endregion
|
|
|
|
#region Step 4 Check if ToSort files are needed for a fix, otherwise delete them or leave them in tosort
|
|
foreach (RvFile inToSortFile in inToSortFiles)
|
|
{
|
|
/*
|
|
// check if we have a confirmed SHA1 / MD5 match of this file, and if we do we just mark this file to be deleted.
|
|
foreach (RvFile correctFile in correctFiles)
|
|
{
|
|
if (!FindSHA1MD5MatchingFiles(inToSortFile, correctFile)) continue;
|
|
inToSortFile.RepStatus = RepStatus.Delete;
|
|
break;
|
|
}
|
|
if (inToSortFile.RepStatus == RepStatus.Delete) continue;
|
|
*/
|
|
|
|
// check if the ToSortFile is needed to fix a missing file
|
|
foreach (RvFile missingFile in missingFiles)
|
|
{
|
|
if (!CheckIfMissingFileCanBeFixedByGotFile(missingFile, inToSortFile)) continue;
|
|
inToSortFile.RepStatus = RepStatus.NeededForFix;
|
|
break;
|
|
}
|
|
if (inToSortFile.RepStatus == RepStatus.NeededForFix) continue;
|
|
|
|
// now that we know this file is not needed for a fix do a CRC only find against correct files to see if this file can be deleted.
|
|
foreach (RvFile correctFile in correctFiles)
|
|
{
|
|
if (!CheckIfGotfileAndMatchingFileAreFullMatches(inToSortFile, correctFile)) continue;
|
|
inToSortFile.RepStatus = RepStatus.Delete;
|
|
break;
|
|
}
|
|
|
|
// otherwise leave the file in ToSort
|
|
}
|
|
#endregion
|
|
|
|
|
|
//need to check here for roms that just need renamed inside the one ZIP
|
|
//this prevents Zips from self deadlocking
|
|
for (int iLoop0 = 0; iLoop0 < length; iLoop0++)
|
|
{
|
|
if (lstRomTableSortedCRC[start + iLoop0].RepStatus != RepStatus.NeededForFix) continue;
|
|
for (int iLoop1 = 0; iLoop1 < length; iLoop1++)
|
|
{
|
|
if (lstRomTableSortedCRC[start + iLoop1].RepStatus != RepStatus.CanBeFixed) continue;
|
|
|
|
if (!CheckIfMissingFileCanBeFixedByGotFile(lstRomTableSortedCRC[start + iLoop1], lstRomTableSortedCRC[start + iLoop0])) continue;
|
|
|
|
if (DBHelper.RomFromSameGame(lstRomTableSortedCRC[start + iLoop0], lstRomTableSortedCRC[start + iLoop1]))
|
|
lstRomTableSortedCRC[start + iLoop0].RepStatus = RepStatus.Rename;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// find fix files, if the gotFile has been fully scanned check the SHA1/MD5, if not then just return true as the CRC/Size is all we have to go on.
|
|
// this means that if the gotfile has not been fully scanned this will return true even with the source and destination SHA1/MD5 possibly different.
|
|
public static bool CheckIfMissingFileCanBeFixedByGotFile(RvFile missingFile, RvFile gotFile)
|
|
{
|
|
|
|
if (missingFile.FileStatusIs(FileStatus.SHA1FromDAT) && gotFile.FileStatusIs(FileStatus.SHA1Verified) && !ArrByte.bCompare(missingFile.SHA1, gotFile.SHA1))
|
|
return false;
|
|
if (missingFile.FileStatusIs(FileStatus.MD5FromDAT) && gotFile.FileStatusIs(FileStatus.MD5Verified) && !ArrByte.bCompare(missingFile.MD5, gotFile.MD5))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
private static bool CheckIfGotfileAndMatchingFileAreFullMatches(RvFile gotFile, RvFile matchingFile)
|
|
{
|
|
if (gotFile.FileStatusIs(FileStatus.SHA1Verified) && matchingFile.FileStatusIs(FileStatus.SHA1Verified) && !ArrByte.bCompare(gotFile.SHA1, matchingFile.SHA1))
|
|
return false;
|
|
if (gotFile.FileStatusIs(FileStatus.MD5Verified) && matchingFile.FileStatusIs(FileStatus.MD5Verified) && !ArrByte.bCompare(gotFile.MD5, matchingFile.MD5))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private static void ListCheckSHA1CHD(List<RvFile> lstRomTableSortedSHA1CHD, int start, int length)
|
|
{
|
|
if (lstRomTableSortedSHA1CHD.Count == 0)
|
|
return;
|
|
|
|
List<RvFile> missingFiles = new List<RvFile>(); // files we done have have that we need
|
|
|
|
List<RvFile> correctFiles = new List<RvFile>(); // files we have that are in the correct place
|
|
List<RvFile> unNeededFiles = new List<RvFile>(); // files we have that are not in the correct place
|
|
List<RvFile> inToSortFiles = new List<RvFile>(); // files we have that are in tosort
|
|
List<RvFile> allGotFiles = new List<RvFile>(); // all files we have
|
|
|
|
List<RvFile> corruptFiles = new List<RvFile>(); // corrupt files that we do not need, a corrupt file is missing if it is needed
|
|
|
|
|
|
// set the found status of this file
|
|
for (int iLoop = 0; iLoop < length; iLoop++)
|
|
{
|
|
RvFile tFile = lstRomTableSortedSHA1CHD[start + iLoop];
|
|
|
|
switch (tFile.RepStatus)
|
|
{
|
|
case RepStatus.Missing:
|
|
missingFiles.Add(tFile); // these are checked in step 1 to fixes from the allGotFiles List.
|
|
break;
|
|
case RepStatus.Correct:
|
|
correctFiles.Add(tFile);
|
|
break;
|
|
case RepStatus.Corrupt:
|
|
case RepStatus.MoveToCorrupt:
|
|
if (tFile.DatStatus == DatStatus.InDatCollect)
|
|
missingFiles.Add(tFile); // corrupt files that are also InDatcollect are treated as missing files, and a fix should be found.
|
|
else
|
|
corruptFiles.Add(tFile); // all other corrupt files should be deleted or moved to tosort/corrupt
|
|
break;
|
|
case RepStatus.UnNeeded:
|
|
case RepStatus.Unknown:
|
|
case RepStatus.MoveToSort:
|
|
case RepStatus.InToSort:
|
|
case RepStatus.Delete:
|
|
case RepStatus.NeededForFix:
|
|
case RepStatus.Rename:
|
|
if (tFile.IsInToSort)
|
|
inToSortFiles.Add(tFile);
|
|
else
|
|
unNeededFiles.Add(tFile);
|
|
break;
|
|
case RepStatus.NotCollected:
|
|
break;
|
|
case RepStatus.Ignore:
|
|
break; // Ignore File
|
|
|
|
default:
|
|
ReportError.SendAndShow(Resources.FindFixes_ListCheck_Unknown_test_status + tFile.DatFullName + Resources.Comma + tFile.DatStatus + Resources.Comma + tFile.RepStatus);
|
|
break;
|
|
|
|
}
|
|
}
|
|
allGotFiles.AddRange(correctFiles);
|
|
allGotFiles.AddRange(unNeededFiles);
|
|
allGotFiles.AddRange(inToSortFiles);
|
|
|
|
|
|
#region Step 1 Check the Missing files from the allGotFiles List.
|
|
// check to see if we can find any of the missing files in the gotFiles list.
|
|
// if we find them mark them as CanBeFixed,
|
|
|
|
foreach (RvFile missingFile in missingFiles)
|
|
{
|
|
if (allGotFiles.Count>0)
|
|
missingFile.RepStatus = (missingFile.RepStatus == RepStatus.Corrupt) || (missingFile.RepStatus == RepStatus.MoveToCorrupt) ? RepStatus.CorruptCanBeFixed : RepStatus.CanBeFixed;
|
|
}
|
|
#endregion
|
|
|
|
#region Step 2 Check all corrupt files.
|
|
// if we have a correct version of the corrupt file then the corrput file can just be deleted,
|
|
// otherwise if the corrupt file is not already in ToSort it should be moved out to ToSort.
|
|
|
|
// we can only check corrupt files using the CRC from the ZIP header, as it is corrupt so we cannot get a correct SHA1 / MD5 to check with
|
|
|
|
foreach (RvFile corruptFile in corruptFiles)
|
|
{
|
|
if (allGotFiles.Count > 0)
|
|
corruptFile.RepStatus = RepStatus.Delete;
|
|
|
|
if (corruptFile.RepStatus == RepStatus.Corrupt && corruptFile.DatStatus != DatStatus.InToSort)
|
|
corruptFile.RepStatus = RepStatus.MoveToCorrupt;
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Step 3 Check if unNeeded files are needed for a fix, otherwise delete them or move them to tosort
|
|
foreach (RvFile unNeededFile in unNeededFiles)
|
|
{
|
|
// check if the unNeededFile is needed to fix a missing file
|
|
if (missingFiles.Count > 0)
|
|
{
|
|
unNeededFile.RepStatus = RepStatus.NeededForFix;
|
|
continue;
|
|
}
|
|
|
|
// now that we know this file is not needed for a fix if we have a correct version of it, it can be deleted.
|
|
if (correctFiles.Count > 0)
|
|
{
|
|
// this probably should check its old state
|
|
if (unNeededFile.RepStatus!=RepStatus.NeededForFix)
|
|
unNeededFile.RepStatus = RepStatus.Delete;
|
|
continue;
|
|
}
|
|
|
|
if (inToSortFiles.Count > 0)
|
|
{
|
|
if (unNeededFile.RepStatus != RepStatus.NeededForFix)
|
|
unNeededFile.RepStatus = RepStatus.Delete;
|
|
continue;
|
|
}
|
|
|
|
// otherwise move the file out to ToSort
|
|
if (unNeededFile.RepStatus != RepStatus.NeededForFix)
|
|
unNeededFile.RepStatus = RepStatus.MoveToSort;
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Step 4 Check if ToSort files are needed for a fix, otherwise delete them or leave them in tosort
|
|
foreach (RvFile inToSortFile in inToSortFiles)
|
|
{
|
|
// check if the ToSortFile is needed to fix a missing file
|
|
if (missingFiles.Count > 0)
|
|
{
|
|
inToSortFile.RepStatus=RepStatus.NeededForFix;
|
|
continue;
|
|
}
|
|
|
|
// now that we know this file is not needed for a fix do a CRC only find against correct files to see if this file can be deleted.
|
|
if (correctFiles.Count <= 0) continue;
|
|
|
|
if (inToSortFile.RepStatus != RepStatus.NeededForFix)
|
|
inToSortFile.RepStatus = RepStatus.Delete;
|
|
|
|
// otherwise leave the file in ToSort
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|