This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
RomVault/ROMVault2/FindFixes.cs
2014-09-23 12:06:15 -07:00

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
}
}
}