[FileTools] Add new unhooked RebuildToOutputAlternate

This commit is contained in:
Matt Nadareski
2016-10-24 17:31:33 -07:00
parent 1d8dc14b98
commit 3043ed5a1a

View File

@@ -14,6 +14,7 @@ using SabreTools.Helper.Skippers;
using NaturalSort; using NaturalSort;
using OCRC; using OCRC;
using SharpCompress.Common;
namespace SabreTools.Helper.Tools namespace SabreTools.Helper.Tools
{ {
@@ -626,8 +627,6 @@ namespace SabreTools.Helper.Tools
/// b) Check against the DAT for duplicates /// b) Check against the DAT for duplicates
/// c) Check for headers /// c) Check for headers
/// d) Check headerless rom for duplicates /// d) Check headerless rom for duplicates
///
/// This is actually rather slow and inefficient. See below for more correct implemenation
/// </remarks> /// </remarks>
public static bool RebuildToOutput(DatFile datFile, List<string> inputs, string outDir, string tempDir, bool quickScan, bool date, public static bool RebuildToOutput(DatFile datFile, List<string> inputs, string outDir, string tempDir, bool quickScan, bool date,
bool toFolder, bool delete, bool tgz, bool romba, ArchiveScanLevel archiveScanLevel, bool updateDat, string headerToCheckAgainst, bool toFolder, bool delete, bool tgz, bool romba, ArchiveScanLevel archiveScanLevel, bool updateDat, string headerToCheckAgainst,
@@ -742,6 +741,341 @@ namespace SabreTools.Helper.Tools
return success; return success;
} }
/// <summary>
/// Process the DAT and find all matches in input files and folders
/// </summary>
/// <param name="datFile">DAT to compare against</param>
/// <param name="inputs">List of input files/folders to check</param>
/// <param name="outDir">Output directory to use to build to</param>
/// <param name="tempDir">Temporary directory for archive extraction</param>
/// <param name="quickScan">True to enable external scanning of archives, false otherwise</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise</param>
/// <param name="toFolder">True if files should be output to folder, false otherwise</param>
/// <param name="delete">True if input files should be deleted, false otherwise</param>
/// <param name="tgz">True if output files should be written to TorrentGZ instead of TorrentZip</param>
/// <param name="romba">True if files should be output in Romba depot folders, false otherwise</param>
/// <param name="archiveScanLevel">ArchiveScanLevel representing the archive handling levels</param>
/// <param name="updateDat">True if the updated DAT should be output, false otherwise</param>
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if rebuilding was a success, false otherwise</returns>
/// <remarks>
/// This is a slightly different implementation of the RebuildToOutput code:
/// 1) Create a DFD from the input files (custom DFD with full pathnames?)
/// 2) Find the files that need to be used for rebuild
/// 3) Order them by game(if not outputting to TGZ)
/// 4) Write out each archive in order
/// </remarks>
public static bool RebuildToOutputAlternate(DatFile datFile, List<string> inputs, string outDir, string tempDir, bool quickScan, bool date,
bool toFolder, bool delete, bool tgz, bool romba, ArchiveScanLevel archiveScanLevel, bool updateDat, string headerToCheckAgainst,
int maxDegreeOfParallelism, Logger logger)
{
// First, check that the output directory exists
if (!Directory.Exists(outDir))
{
Directory.CreateDirectory(outDir);
outDir = Path.GetFullPath(outDir);
}
// Then create or clean the temp directory
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
else
{
CleanDirectory(tempDir);
}
bool success = true;
DatFile matched = new DatFile();
List<string> files = new List<string>();
#region Retrieve a list of all files
logger.User("Retrieving list all files from input");
DateTime start = DateTime.Now;
// Create a list of just files from inputs
Parallel.ForEach(inputs,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism, },
input => {
if (File.Exists(input))
{
logger.Verbose("File found: '" + input + "'");
files.Add(Path.GetFullPath(input));
}
else if (Directory.Exists(input))
{
logger.Verbose("Directory found: '" + input + "'");
Parallel.ForEach(Directory.EnumerateFiles(input, "*", SearchOption.AllDirectories),
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism, },
file =>
{
logger.Verbose("File found: '" + file + "'");
files.Add(Path.GetFullPath(file));
});
}
else
{
logger.Error("'" + input + "' is not a file or directory!");
}
});
logger.User("Retrieving complete in: " + DateTime.Now.Subtract(start).ToString(@"hh\:mm\:ss\.fffff"));
#endregion
DatFile current = new DatFile();
#region Create a dat from input files
// Now that we have a list of just files, we get a DAT from the input files
foreach (string file in files)
{
// Define the temporary directory
string tempSubDir = Path.GetFullPath(Path.Combine(tempDir, Path.GetRandomFileName())) + Path.DirectorySeparatorChar;
// Get the required scanning level for the file
bool shouldExternalProcess = false;
bool shouldInternalProcess = false;
ArchiveTools.GetInternalExternalProcess(file, archiveScanLevel, logger, out shouldExternalProcess, out shouldInternalProcess);
// If we're supposed to scan the file externally
if (shouldExternalProcess)
{
Rom rom = GetFileInfo(file, logger, noMD5: quickScan, noSHA1: quickScan, header: headerToCheckAgainst);
rom.Name = Path.GetFullPath(file);
string key = rom.Size + "-" + rom.CRC;
if (current.Files.ContainsKey(key))
{
current.Files[key].Add(rom);
}
else
{
List<DatItem> temp = new List<DatItem>();
temp.Add(rom);
current.Files.Add(key, temp);
}
}
// If we're supposed to scan the file internally
if (shouldInternalProcess)
{
// If quickscan is set, do so
if (quickScan)
{
List<Rom> extracted = ArchiveTools.GetArchiveFileInfo(file, logger);
foreach (Rom rom in extracted)
{
Rom newrom = rom;
newrom.Machine = new Machine(Path.GetFullPath(file), "");
string key = rom.Size + "-" + rom.CRC;
if (current.Files.ContainsKey(key))
{
current.Files[key].Add(newrom);
}
else
{
List<DatItem> temp = new List<DatItem>();
temp.Add(newrom);
current.Files.Add(key, temp);
}
}
}
// Otherwise, attempt to extract the files to the temporary directory
else
{
bool encounteredErrors = ArchiveTools.ExtractArchive(file, tempSubDir, archiveScanLevel, logger);
// If the file was an archive and was extracted successfully, check it
if (!encounteredErrors)
{
logger.Verbose(Path.GetFileName(file) + " treated like an archive");
List<string> extracted = Directory.EnumerateFiles(tempSubDir, "*", SearchOption.AllDirectories).ToList();
Parallel.ForEach(extracted,
new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism },
entry =>
{
Rom rom = GetFileInfo(entry, logger, noMD5: quickScan, noSHA1: quickScan, header: headerToCheckAgainst);
rom.Name = Path.Combine(Path.GetFullPath(file), Path.GetFileName(entry));
rom.Machine = new Machine(Path.GetFullPath(file), "");
string key = rom.Size + "-" + rom.CRC;
if (current.Files.ContainsKey(key))
{
current.Files[key].Add(rom);
}
else
{
List<DatItem> temp = new List<DatItem>();
temp.Add(rom);
current.Files.Add(key, temp);
}
});
}
// Otherwise, just get the info on the file itself
else if (File.Exists(file))
{
Rom rom = GetFileInfo(file, logger, noMD5: quickScan, noSHA1: quickScan, header: headerToCheckAgainst);
rom.Name = Path.GetFullPath(file);
string key = rom.Size + "-" + rom.CRC;
if (current.Files.ContainsKey(key))
{
current.Files[key].Add(rom);
}
else
{
List<DatItem> temp = new List<DatItem>();
temp.Add(rom);
current.Files.Add(key, temp);
}
}
}
}
// Now delete the temp directory
try
{
Directory.Delete(tempSubDir, true);
}
catch { }
}
#endregion
// Create a mapping from destination file to source file
Dictionary<DatItem, DatItem> toFromMap = new Dictionary<DatItem, DatItem>();
#region Find all required files for rebuild
// Order the DATs by hash first to make things easier
datFile.BucketByCRC(false, logger, output: false);
current.BucketByCRC(false, logger, output: false);
// Now loop over and find all files that need to be rebuilt
List<string> keys = current.Files.Keys.ToList();
foreach (string key in keys)
{
// If the input DAT doesn't have the key, then nothing from the current DAT are there
if (!datFile.Files.ContainsKey(key))
{
continue;
}
// Otherwise, we try to find duplicates
List<DatItem> datItems = current.Files[key];
foreach (Rom rom in datItems)
{
List<DatItem> found = rom.GetDuplicates(datFile, logger, false);
// Now add all of the duplicates mapped to the current file
foreach (Rom mid in found)
{
try
{
toFromMap.Add(mid, rom);
}
catch { }
}
}
}
#endregion
// Now bucket the list of keys by game so that we can rebuild properly
SortedDictionary<string, List<DatItem>> keysGroupedByGame = DatFile.BucketListByGame(toFromMap.Keys.ToList(), false, true, logger, output: false);
#region Rebuild games in order
// Now loop through the keys and create the correct output items
List<string> games = keysGroupedByGame.Keys.ToList();
foreach (string game in games)
{
// Define the temporary directory
string tempSubDir = Path.GetFullPath(Path.Combine(tempDir, Path.GetRandomFileName())) + Path.DirectorySeparatorChar;
// Create an empty list for getting paths for rebuilding
List<string> pathsToFiles = new List<string>();
// Loop through all of the matched items in the game
List<DatItem> itemsInGame = keysGroupedByGame[game];
List<Rom> romsInGame = new List<Rom>();
foreach (Rom rom in itemsInGame)
{
// Get the rom that's mapped to this item
Rom source = (Rom)toFromMap[rom];
// If the file is in an archive, we need to treat it specially
string machinename = source.Machine.Name.ToLowerInvariant();
if (machinename.EndsWith(".7z")
|| machinename.EndsWith(".gz")
|| machinename.EndsWith(".rar")
|| machinename.EndsWith(".zip"))
{
pathsToFiles.Add(ArchiveTools.ExtractItem(source.Machine.Name, source.Name, tempSubDir, logger));
}
// Otherwise, we want to just add the full path
else
{
pathsToFiles.Add(source.Name);
}
// Now add the rom to the output list
romsInGame.Add(source);
}
// And now rebuild accordingly
if (toFolder)
{
for (int i = 0; i < romsInGame.Count; i++)
{
string infile = pathsToFiles[i];
Rom outrom = romsInGame[i];
string outfile = Path.Combine(outDir, outrom.Machine.Name, outrom.Machine.Name);
// Make sure the output folder is created
Directory.CreateDirectory(Path.GetDirectoryName(outfile));
// Now copy the file over
try
{
File.Copy(infile, outfile);
}
catch { }
}
}
else if (tgz)
{
for (int i = 0; i < itemsInGame.Count; i++)
{
string infile = pathsToFiles[i];
Rom outrom = romsInGame[i];
ArchiveTools.WriteTorrentGZ(infile, outDir, romba, logger);
}
}
else
{
ArchiveTools.WriteToArchive(pathsToFiles, outDir, romsInGame, logger);
}
// And now clear the temp folder to get rid of any transient files
try
{
Directory.Delete(tempSubDir, true);
}
catch { }
}
#endregion
return success;
}
/// <summary> /// <summary>
/// Process an individual file against the DAT for rebuilding /// Process an individual file against the DAT for rebuilding
/// </summary> /// </summary>