diff --git a/SabreTools.Helper/SabreTools.Helper.csproj b/SabreTools.Helper/SabreTools.Helper.csproj
index c94fd167..ab31812a 100644
--- a/SabreTools.Helper/SabreTools.Helper.csproj
+++ b/SabreTools.Helper/SabreTools.Helper.csproj
@@ -118,7 +118,9 @@
+
+
diff --git a/SabreTools.Helper/Tools/DatToolsHash.cs b/SabreTools.Helper/Tools/DatToolsHash.cs
index 821accf3..2022e4b4 100644
--- a/SabreTools.Helper/Tools/DatToolsHash.cs
+++ b/SabreTools.Helper/Tools/DatToolsHash.cs
@@ -1428,7 +1428,7 @@ namespace SabreTools.Helper
List hashes = dict[key];
if (mergeroms)
{
- hashes = RomTools.Merge(hashes, logger);
+ hashes = RomToolsHash.Merge(hashes, logger);
}
foreach (HashData hash in hashes)
@@ -1505,7 +1505,7 @@ namespace SabreTools.Helper
List newhashes = hashes;
if (mergeroms)
{
- newhashes = RomTools.Merge(newhashes, logger);
+ newhashes = RomToolsHash.Merge(newhashes, logger);
}
foreach (HashData hash in newhashes)
diff --git a/SabreTools.Helper/Tools/FileTools.cs b/SabreTools.Helper/Tools/FileTools.cs
index 7460488f..a58f6fb1 100644
--- a/SabreTools.Helper/Tools/FileTools.cs
+++ b/SabreTools.Helper/Tools/FileTools.cs
@@ -1093,291 +1093,5 @@ namespace SabreTools.Helper
}
#endregion
-
- #region Hash-to-Dat functions
-
- // All things in this region are direct ports and do not take advantage of the multiple rom per hash that comes with the new system
-
- ///
- /// Copy a file to an output archive
- ///
- /// Input filename to be moved
- /// Output directory to build to
- /// RomData representing the new information
- /// This uses the new system that is not implemented anywhere yet
- public static void WriteToArchive(string input, string output, RomData rom)
- {
- string archiveFileName = Path.Combine(output, rom.Machine + ".zip");
-
- ZipArchive outarchive = null;
- try
- {
- if (!File.Exists(archiveFileName))
- {
- outarchive = ZipFile.Open(archiveFileName, ZipArchiveMode.Create);
- }
- else
- {
- outarchive = ZipFile.Open(archiveFileName, ZipArchiveMode.Update);
- }
-
- if (File.Exists(input))
- {
- if (outarchive.Mode == ZipArchiveMode.Create || outarchive.GetEntry(rom.Name) == null)
- {
- outarchive.CreateEntryFromFile(input, rom.Name, CompressionLevel.Optimal);
- }
- }
- else if (Directory.Exists(input))
- {
- foreach (string file in Directory.EnumerateFiles(input, "*", SearchOption.AllDirectories))
- {
- if (outarchive.Mode == ZipArchiveMode.Create || outarchive.GetEntry(file) == null)
- {
- outarchive.CreateEntryFromFile(file, file, CompressionLevel.Optimal);
- }
- }
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex);
- }
- finally
- {
- outarchive?.Dispose();
- }
- }
-
- ///
- /// Copy a file to an output archive using SharpCompress
- ///
- /// Input filename to be moved
- /// Output directory to build to
- /// RomData representing the new information
- /// This uses the new system that is not implemented anywhere yet
- public static void WriteToManagedArchive(string input, string output, RomData rom)
- {
- string archiveFileName = Path.Combine(output, rom.Machine + ".zip");
-
- // Delete an empty file first
- if (File.Exists(archiveFileName) && new FileInfo(archiveFileName).Length == 0)
- {
- File.Delete(archiveFileName);
- }
-
- // Get if the file should be written out
- bool newfile = File.Exists(archiveFileName) && new FileInfo(archiveFileName).Length != 0;
-
- using (SharpCompress.Archive.Zip.ZipArchive archive = (newfile
- ? ArchiveFactory.Open(archiveFileName, Options.LookForHeader) as SharpCompress.Archive.Zip.ZipArchive
- : ArchiveFactory.Create(ArchiveType.Zip) as SharpCompress.Archive.Zip.ZipArchive))
- {
- try
- {
- if (File.Exists(input))
- {
- archive.AddEntry(rom.Name, input);
- }
- else if (Directory.Exists(input))
- {
- archive.AddAllFromDirectory(input, "*", SearchOption.AllDirectories);
- }
-
- archive.SaveTo(archiveFileName + ".tmp", CompressionType.Deflate);
- }
- catch (Exception)
- {
- // Don't log archive write errors
- }
- }
-
- if (File.Exists(archiveFileName + ".tmp"))
- {
- File.Delete(archiveFileName);
- File.Move(archiveFileName + ".tmp", archiveFileName);
- }
- }
-
- ///
- /// Generate a list of HashData objects from the header values in an archive
- ///
- /// Input file to get data from
- /// Logger object for file and console output
- /// List of HashData objects representing the found data
- /// This uses the new system that is not implemented anywhere yet
- public static List GetArchiveFileHashes(string input, Logger logger)
- {
- List hashes = new List();
- string gamename = Path.GetFileNameWithoutExtension(input);
-
- // First get the archive type
- ArchiveType? at = GetCurrentArchiveType(input, logger);
-
- // If we got back null, then it's not an archive, so we we return
- if (at == null)
- {
- return hashes;
- }
-
- // If we got back GZip, try to get TGZ info first
- else if (at == ArchiveType.GZip)
- {
- HashData possibleTgz = GetTorrentGZFileHash(input, logger);
-
- // If it was, then add it to the outputs and continue
- if (possibleTgz.Size != -1)
- {
- hashes.Add(possibleTgz);
- return hashes;
- }
- }
-
- IReader reader = null;
- try
- {
- logger.Log("Found archive of type: " + at);
- long size = 0;
- byte[] crc = null;
-
- // If we have a gzip file, get the crc directly
- if (at == ArchiveType.GZip)
- {
- // Get the CRC and size from the file
- using (BinaryReader br = new BinaryReader(File.OpenRead(input)))
- {
- br.BaseStream.Seek(-8, SeekOrigin.End);
- crc = br.ReadBytes(4).Reverse().ToArray();
- byte[] headersize = br.ReadBytes(4);
- size = BitConverter.ToInt32(headersize.Reverse().ToArray(), 0);
- }
- }
-
- reader = ReaderFactory.Open(File.OpenRead(input));
-
- if (at != ArchiveType.Tar)
- {
- while (reader.MoveToNextEntry())
- {
- if (reader.Entry != null && !reader.Entry.IsDirectory)
- {
- logger.Log("Entry found: '" + reader.Entry.Key + "': "
- + (size == 0 ? reader.Entry.Size : size) + ", "
- + (crc == null ? BitConverter.GetBytes(reader.Entry.Crc) : crc));
-
- RomData temprom = new RomData
- {
- Type = ItemType.Rom,
- Name = reader.Entry.Key,
- Machine = new MachineData
- {
- Name = gamename,
- Description = gamename,
- },
- };
- HashData temphash = new HashData
- {
- Size = (size == 0 ? reader.Entry.Size : size),
- CRC = (crc == null ? BitConverter.GetBytes(reader.Entry.Crc) : crc),
- MD5 = null,
- SHA1 = null,
- Roms = new List(),
- };
- temphash.Roms.Add(temprom);
- hashes.Add(temphash);
- }
- }
- }
- }
- catch (Exception ex)
- {
- logger.Error(ex.ToString());
- }
- finally
- {
- reader?.Dispose();
- }
-
- return hashes;
- }
-
- ///
- /// Retrieve file information for a single torrent GZ file
- ///
- /// Filename to get information from
- /// Logger object for file and console output
- /// Populated HashData object if success, empty one on error
- /// This uses the new system that is not implemented anywhere yet
- public static HashData GetTorrentGZFileHash(string input, Logger logger)
- {
- string datum = Path.GetFileName(input).ToLowerInvariant();
- string sha1 = Path.GetFileNameWithoutExtension(input).ToLowerInvariant();
- long filesize = new FileInfo(input).Length;
-
- // Check if the name is the right length
- if (!Regex.IsMatch(datum, @"^[0-9a-f]{40}\.gz"))
- {
- logger.Warning("Non SHA-1 filename found, skipping: '" + datum + "'");
- return new HashData();
- }
-
- // Check if the file is at least the minimum length
- if (filesize < 40 /* bytes */)
- {
- logger.Warning("Possibly corrupt file '" + input + "' with size " + Style.GetBytesReadable(filesize));
- return new HashData();
- }
-
- // Get the Romba-specific header data
- byte[] header; // Get preamble header for checking
- byte[] headermd5; // MD5
- byte[] headercrc; // CRC
- byte[] headersz; // Int64 size
- using (BinaryReader br = new BinaryReader(File.OpenRead(input)))
- {
- header = br.ReadBytes(12);
- headermd5 = br.ReadBytes(16);
- headercrc = br.ReadBytes(4);
- headersz = br.ReadBytes(8);
- }
-
- // If the header is not correct, return a blank rom
- bool correct = true;
- for (int i = 0; i < header.Length; i++)
- {
- correct &= (header[i] == Constants.TorrentGZHeader[i]);
- }
- if (!correct)
- {
- return new HashData();
- }
-
- // Now convert the size and get the right position
- long extractedsize = (long)BitConverter.ToUInt64(headersz.Reverse().ToArray(), 0);
-
- RomData temprom = new RomData
- {
- Type = ItemType.Rom,
- Name = sha1,
- Machine = new MachineData
- {
- Name = sha1,
- Description = sha1,
- },
- };
- HashData temphash = new HashData
- {
- Size = extractedsize,
- CRC = headercrc,
- MD5 = headermd5,
- SHA1 = Style.StringToByteArray(sha1),
- Roms = new List(),
- };
- temphash.Roms.Add(temprom);
-
- return temphash;
- }
-
- #endregion
}
}
diff --git a/SabreTools.Helper/Tools/FileToolsHash.cs b/SabreTools.Helper/Tools/FileToolsHash.cs
new file mode 100644
index 00000000..efe9de6b
--- /dev/null
+++ b/SabreTools.Helper/Tools/FileToolsHash.cs
@@ -0,0 +1,305 @@
+using SharpCompress.Archive;
+using SharpCompress.Common;
+using SharpCompress.Reader;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace SabreTools.Helper
+{
+ public class FileToolsHash
+ {
+ #region Archive Writing
+
+ // All things in this region are direct ports and do not take advantage of the multiple rom per hash that comes with the new system
+
+ ///
+ /// Copy a file to an output archive
+ ///
+ /// Input filename to be moved
+ /// Output directory to build to
+ /// RomData representing the new information
+ /// This uses the new system that is not implemented anywhere yet
+ public static void WriteToArchive(string input, string output, RomData rom)
+ {
+ string archiveFileName = Path.Combine(output, rom.Machine + ".zip");
+
+ ZipArchive outarchive = null;
+ try
+ {
+ if (!File.Exists(archiveFileName))
+ {
+ outarchive = ZipFile.Open(archiveFileName, ZipArchiveMode.Create);
+ }
+ else
+ {
+ outarchive = ZipFile.Open(archiveFileName, ZipArchiveMode.Update);
+ }
+
+ if (File.Exists(input))
+ {
+ if (outarchive.Mode == ZipArchiveMode.Create || outarchive.GetEntry(rom.Name) == null)
+ {
+ outarchive.CreateEntryFromFile(input, rom.Name, CompressionLevel.Optimal);
+ }
+ }
+ else if (Directory.Exists(input))
+ {
+ foreach (string file in Directory.EnumerateFiles(input, "*", SearchOption.AllDirectories))
+ {
+ if (outarchive.Mode == ZipArchiveMode.Create || outarchive.GetEntry(file) == null)
+ {
+ outarchive.CreateEntryFromFile(file, file, CompressionLevel.Optimal);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ }
+ finally
+ {
+ outarchive?.Dispose();
+ }
+ }
+
+ ///
+ /// Copy a file to an output archive using SharpCompress
+ ///
+ /// Input filename to be moved
+ /// Output directory to build to
+ /// RomData representing the new information
+ /// This uses the new system that is not implemented anywhere yet
+ public static void WriteToManagedArchive(string input, string output, RomData rom)
+ {
+ string archiveFileName = Path.Combine(output, rom.Machine + ".zip");
+
+ // Delete an empty file first
+ if (File.Exists(archiveFileName) && new FileInfo(archiveFileName).Length == 0)
+ {
+ File.Delete(archiveFileName);
+ }
+
+ // Get if the file should be written out
+ bool newfile = File.Exists(archiveFileName) && new FileInfo(archiveFileName).Length != 0;
+
+ using (SharpCompress.Archive.Zip.ZipArchive archive = (newfile
+ ? ArchiveFactory.Open(archiveFileName, Options.LookForHeader) as SharpCompress.Archive.Zip.ZipArchive
+ : ArchiveFactory.Create(ArchiveType.Zip) as SharpCompress.Archive.Zip.ZipArchive))
+ {
+ try
+ {
+ if (File.Exists(input))
+ {
+ archive.AddEntry(rom.Name, input);
+ }
+ else if (Directory.Exists(input))
+ {
+ archive.AddAllFromDirectory(input, "*", SearchOption.AllDirectories);
+ }
+
+ archive.SaveTo(archiveFileName + ".tmp", CompressionType.Deflate);
+ }
+ catch (Exception)
+ {
+ // Don't log archive write errors
+ }
+ }
+
+ if (File.Exists(archiveFileName + ".tmp"))
+ {
+ File.Delete(archiveFileName);
+ File.Move(archiveFileName + ".tmp", archiveFileName);
+ }
+ }
+
+ #endregion
+
+ #region File Information
+
+ ///
+ /// Generate a list of HashData objects from the header values in an archive
+ ///
+ /// Input file to get data from
+ /// Logger object for file and console output
+ /// List of HashData objects representing the found data
+ /// This uses the new system that is not implemented anywhere yet
+ public static List GetArchiveFileHashes(string input, Logger logger)
+ {
+ List hashes = new List();
+ string gamename = Path.GetFileNameWithoutExtension(input);
+
+ // First get the archive type
+ ArchiveType? at = FileTools.GetCurrentArchiveType(input, logger);
+
+ // If we got back null, then it's not an archive, so we we return
+ if (at == null)
+ {
+ return hashes;
+ }
+
+ // If we got back GZip, try to get TGZ info first
+ else if (at == ArchiveType.GZip)
+ {
+ HashData possibleTgz = GetTorrentGZFileHash(input, logger);
+
+ // If it was, then add it to the outputs and continue
+ if (possibleTgz.Size != -1)
+ {
+ hashes.Add(possibleTgz);
+ return hashes;
+ }
+ }
+
+ IReader reader = null;
+ try
+ {
+ logger.Log("Found archive of type: " + at);
+ long size = 0;
+ byte[] crc = null;
+
+ // If we have a gzip file, get the crc directly
+ if (at == ArchiveType.GZip)
+ {
+ // Get the CRC and size from the file
+ using (BinaryReader br = new BinaryReader(File.OpenRead(input)))
+ {
+ br.BaseStream.Seek(-8, SeekOrigin.End);
+ crc = br.ReadBytes(4).Reverse().ToArray();
+ byte[] headersize = br.ReadBytes(4);
+ size = BitConverter.ToInt32(headersize.Reverse().ToArray(), 0);
+ }
+ }
+
+ reader = ReaderFactory.Open(File.OpenRead(input));
+
+ if (at != ArchiveType.Tar)
+ {
+ while (reader.MoveToNextEntry())
+ {
+ if (reader.Entry != null && !reader.Entry.IsDirectory)
+ {
+ logger.Log("Entry found: '" + reader.Entry.Key + "': "
+ + (size == 0 ? reader.Entry.Size : size) + ", "
+ + (crc == null ? BitConverter.GetBytes(reader.Entry.Crc) : crc));
+
+ RomData temprom = new RomData
+ {
+ Type = ItemType.Rom,
+ Name = reader.Entry.Key,
+ Machine = new MachineData
+ {
+ Name = gamename,
+ Description = gamename,
+ },
+ };
+ HashData temphash = new HashData
+ {
+ Size = (size == 0 ? reader.Entry.Size : size),
+ CRC = (crc == null ? BitConverter.GetBytes(reader.Entry.Crc) : crc),
+ MD5 = null,
+ SHA1 = null,
+ Roms = new List(),
+ };
+ temphash.Roms.Add(temprom);
+ hashes.Add(temphash);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.Error(ex.ToString());
+ }
+ finally
+ {
+ reader?.Dispose();
+ }
+
+ return hashes;
+ }
+
+ ///
+ /// Retrieve file information for a single torrent GZ file
+ ///
+ /// Filename to get information from
+ /// Logger object for file and console output
+ /// Populated HashData object if success, empty one on error
+ /// This uses the new system that is not implemented anywhere yet
+ public static HashData GetTorrentGZFileHash(string input, Logger logger)
+ {
+ string datum = Path.GetFileName(input).ToLowerInvariant();
+ string sha1 = Path.GetFileNameWithoutExtension(input).ToLowerInvariant();
+ long filesize = new FileInfo(input).Length;
+
+ // Check if the name is the right length
+ if (!Regex.IsMatch(datum, @"^[0-9a-f]{40}\.gz"))
+ {
+ logger.Warning("Non SHA-1 filename found, skipping: '" + datum + "'");
+ return new HashData();
+ }
+
+ // Check if the file is at least the minimum length
+ if (filesize < 40 /* bytes */)
+ {
+ logger.Warning("Possibly corrupt file '" + input + "' with size " + Style.GetBytesReadable(filesize));
+ return new HashData();
+ }
+
+ // Get the Romba-specific header data
+ byte[] header; // Get preamble header for checking
+ byte[] headermd5; // MD5
+ byte[] headercrc; // CRC
+ byte[] headersz; // Int64 size
+ using (BinaryReader br = new BinaryReader(File.OpenRead(input)))
+ {
+ header = br.ReadBytes(12);
+ headermd5 = br.ReadBytes(16);
+ headercrc = br.ReadBytes(4);
+ headersz = br.ReadBytes(8);
+ }
+
+ // If the header is not correct, return a blank rom
+ bool correct = true;
+ for (int i = 0; i < header.Length; i++)
+ {
+ correct &= (header[i] == Constants.TorrentGZHeader[i]);
+ }
+ if (!correct)
+ {
+ return new HashData();
+ }
+
+ // Now convert the size and get the right position
+ long extractedsize = (long)BitConverter.ToUInt64(headersz.Reverse().ToArray(), 0);
+
+ RomData temprom = new RomData
+ {
+ Type = ItemType.Rom,
+ Name = sha1,
+ Machine = new MachineData
+ {
+ Name = sha1,
+ Description = sha1,
+ },
+ };
+ HashData temphash = new HashData
+ {
+ Size = extractedsize,
+ CRC = headercrc,
+ MD5 = headermd5,
+ SHA1 = Style.StringToByteArray(sha1),
+ Roms = new List(),
+ };
+ temphash.Roms.Add(temprom);
+
+ return temphash;
+ }
+
+ #endregion
+ }
+}
diff --git a/SabreTools.Helper/Tools/RomTools.cs b/SabreTools.Helper/Tools/RomTools.cs
index f495a079..4f15ebca 100644
--- a/SabreTools.Helper/Tools/RomTools.cs
+++ b/SabreTools.Helper/Tools/RomTools.cs
@@ -245,223 +245,5 @@ namespace SabreTools.Helper
}
#endregion
-
- #region HashData-based sorting and merging
-
- ///
- /// Merge an arbitrary set of ROMs based on the supplied information
- ///
- /// List of HashData objects representing the roms to be merged
- /// Logger object for console and/or file output
- /// A List of HashData objects representing the merged roms
- public static List Merge(List inhashes, Logger logger)
- {
- // Create output list
- List outroms = new List();
-
- // Check for null or blank roms first
- if (inhashes == null || inhashes.Count == 0)
- {
- return outroms;
- }
-
- // Then deduplicate them by checking to see if data matches previous saved roms
- foreach (HashData hash in inhashes)
- {
- // If it's a nodump, add and skip
- if (hash.Roms[0].Nodump)
- {
- outroms.Add(hash);
- continue;
- }
-
- // If it's the first rom in the list, don't touch it
- if (outroms.Count != 0)
- {
- // Check if the rom is a duplicate
- DupeType dupetype = DupeType.None;
- HashData savedHash = new HashData();
- int pos = -1;
- for (int i = 0; i < outroms.Count; i++)
- {
- HashData lastrom = outroms[i];
- RomData savedRom = savedHash.Roms[0];
- MachineData savedMachine = savedRom.Machine;
-
- // Get the duplicate status
- dupetype = GetDuplicateStatus(hash, lastrom, logger);
-
- // If it's a duplicate, skip adding it to the output but add any missing information
- if (dupetype != DupeType.None)
- {
- savedHash = lastrom;
- pos = i;
-
- savedHash.CRC = (savedHash.CRC == null && hash.CRC != null ? hash.CRC : savedHash.CRC);
- savedHash.MD5 = (savedHash.MD5 == null && hash.MD5 != null ? hash.MD5 : savedHash.MD5);
- savedHash.SHA1 = (savedHash.SHA1 == null && hash.SHA1 != null ? hash.SHA1 : savedHash.SHA1);
- savedRom.DupeType = dupetype;
-
- // If the current system has a lower ID than the previous, set the system accordingly
- if (hash.Roms[0].Machine.SystemID < savedMachine.SystemID)
- {
- savedMachine.SystemID = hash.Roms[0].Machine.SystemID;
- savedMachine.System = hash.Roms[0].Machine.System;
- savedMachine.Name = hash.Roms[0].Machine.Name;
- savedRom.Name = hash.Roms[0].Name;
- }
-
- // If the current source has a lower ID than the previous, set the source accordingly
- if (hash.Roms[0].Machine.SourceID < savedMachine.SourceID)
- {
- savedMachine.SourceID = hash.Roms[0].Machine.SourceID;
- savedMachine.Source = hash.Roms[0].Machine.Source;
- savedMachine.Name = hash.Roms[0].Machine.Name;
- savedRom.Name = hash.Roms[0].Name;
- }
-
- break;
- }
- }
-
- // If no duplicate is found, add it to the list
- if (dupetype == DupeType.None)
- {
- outroms.Add(hash);
- }
- // Otherwise, if a new rom information is found, add that
- else
- {
- outroms.RemoveAt(pos);
- outroms.Insert(pos, savedHash);
- }
- }
- else
- {
- outroms.Add(hash);
- }
- }
-
- // Then return the result
- return outroms;
- }
-
- /*
- ///
- /// List all duplicates found in a DAT based on a rom
- ///
- /// Hash to use as a base
- /// DAT to match against
- /// Logger object for console and/or file output
- /// True to remove matched roms from the input, false otherwise (default)
- /// List of matched HashData objects
- public static List GetDuplicates(HashData lastrom, DatData datdata, Logger logger, bool remove = false)
- {
- List output = new List();
-
- // Check for an empty rom list first
- if (datdata.Hashes == null || datdata.Hashes.Count == 0)
- {
- return output;
- }
-
- // Try to find duplicates
- for (int i = 0; i < datdata.Hashes.Count; i++)
- {
-
- List roms = datdata.Files[key];
- List left = new List();
- foreach (Rom rom in roms)
- {
- if (IsDuplicate(rom, lastrom, logger))
- {
- output.Add(rom);
- }
- else
- {
- left.Add(rom);
- }
- }
-
- // If we're in removal mode, replace the list with the new one
- if (remove)
- {
- datdata.Files[key] = left;
- }
- }
-
- return output;
- }
- */
-
- ///
- /// Determine if a file is a duplicate using partial matching logic
- ///
- /// Hash to check for duplicate status
- /// Hash to use as a baseline
- /// Logger object for console and/or file output
- /// True if the hashes are duplicates, false otherwise
- public static bool IsDuplicate(HashData hash, int hashIndex, HashData lastHash, int lastHashIndex, Logger logger)
- {
- bool dupefound = hash.Equals(lastHash);
-
- // More wonderful SHA-1 logging that has to be done
- if (hash.SHA1 == lastHash.SHA1 && hash.Size != lastHash.Size)
- {
- logger.User("SHA-1 mismatch - Hash: " + hash.SHA1);
- }
-
- return dupefound;
- }
-
- ///
- /// Return the duplicate status of two hashes
- ///
- /// Current hash to check
- /// Last hash to check against
- /// Logger object for console and/or file output
- /// The DupeType corresponding to the relationship between the two
- public static DupeType GetDuplicateStatus(HashData hash, HashData lasthash, Logger logger)
- {
- DupeType output = DupeType.None;
-
- /*
- // If we don't have a duplicate at all, return none
- if (!IsDuplicate(hash, lasthash, logger))
- {
- return output;
- }
- */
-
- // If the duplicate is external already or should be, set it
- if (lasthash.Roms[0].DupeType >= DupeType.ExternalHash || lasthash.Roms[0].Machine.SystemID != hash.Roms[0].Machine.SystemID || lasthash.Roms[0].Machine.SourceID != hash.Roms[0].Machine.SourceID)
- {
- if (lasthash.Roms[0].Machine.Name == hash.Roms[0].Machine.Name && lasthash.Roms[0].Name == hash.Roms[0].Name)
- {
- output = DupeType.ExternalAll;
- }
- else
- {
- output = DupeType.ExternalHash;
- }
- }
-
- // Otherwise, it's considered an internal dupe
- else
- {
- if (lasthash.Roms[0].Machine.Name == hash.Roms[0].Machine.Name && lasthash.Roms[0].Name == hash.Roms[0].Name)
- {
- output = DupeType.InternalAll;
- }
- else
- {
- output = DupeType.InternalHash;
- }
- }
-
- return output;
- }
-
- #endregion
}
}
diff --git a/SabreTools.Helper/Tools/RomToolsHash.cs b/SabreTools.Helper/Tools/RomToolsHash.cs
new file mode 100644
index 00000000..632295c2
--- /dev/null
+++ b/SabreTools.Helper/Tools/RomToolsHash.cs
@@ -0,0 +1,225 @@
+using System.Collections.Generic;
+
+namespace SabreTools.Helper
+{
+ public class RomToolsHash
+ {
+ #region HashData-based sorting and merging
+
+ ///
+ /// Merge an arbitrary set of ROMs based on the supplied information
+ ///
+ /// List of HashData objects representing the roms to be merged
+ /// Logger object for console and/or file output
+ /// A List of HashData objects representing the merged roms
+ public static List Merge(List inhashes, Logger logger)
+ {
+ // Create output list
+ List outroms = new List();
+
+ // Check for null or blank roms first
+ if (inhashes == null || inhashes.Count == 0)
+ {
+ return outroms;
+ }
+
+ // Then deduplicate them by checking to see if data matches previous saved roms
+ foreach (HashData hash in inhashes)
+ {
+ // If it's a nodump, add and skip
+ if (hash.Roms[0].Nodump)
+ {
+ outroms.Add(hash);
+ continue;
+ }
+
+ // If it's the first rom in the list, don't touch it
+ if (outroms.Count != 0)
+ {
+ // Check if the rom is a duplicate
+ DupeType dupetype = DupeType.None;
+ HashData savedHash = new HashData();
+ int pos = -1;
+ for (int i = 0; i < outroms.Count; i++)
+ {
+ HashData lastrom = outroms[i];
+ RomData savedRom = savedHash.Roms[0];
+ MachineData savedMachine = savedRom.Machine;
+
+ // Get the duplicate status
+ dupetype = GetDuplicateStatus(hash, lastrom, logger);
+
+ // If it's a duplicate, skip adding it to the output but add any missing information
+ if (dupetype != DupeType.None)
+ {
+ savedHash = lastrom;
+ pos = i;
+
+ savedHash.CRC = (savedHash.CRC == null && hash.CRC != null ? hash.CRC : savedHash.CRC);
+ savedHash.MD5 = (savedHash.MD5 == null && hash.MD5 != null ? hash.MD5 : savedHash.MD5);
+ savedHash.SHA1 = (savedHash.SHA1 == null && hash.SHA1 != null ? hash.SHA1 : savedHash.SHA1);
+ savedRom.DupeType = dupetype;
+
+ // If the current system has a lower ID than the previous, set the system accordingly
+ if (hash.Roms[0].Machine.SystemID < savedMachine.SystemID)
+ {
+ savedMachine.SystemID = hash.Roms[0].Machine.SystemID;
+ savedMachine.System = hash.Roms[0].Machine.System;
+ savedMachine.Name = hash.Roms[0].Machine.Name;
+ savedRom.Name = hash.Roms[0].Name;
+ }
+
+ // If the current source has a lower ID than the previous, set the source accordingly
+ if (hash.Roms[0].Machine.SourceID < savedMachine.SourceID)
+ {
+ savedMachine.SourceID = hash.Roms[0].Machine.SourceID;
+ savedMachine.Source = hash.Roms[0].Machine.Source;
+ savedMachine.Name = hash.Roms[0].Machine.Name;
+ savedRom.Name = hash.Roms[0].Name;
+ }
+
+ break;
+ }
+ }
+
+ // If no duplicate is found, add it to the list
+ if (dupetype == DupeType.None)
+ {
+ outroms.Add(hash);
+ }
+ // Otherwise, if a new rom information is found, add that
+ else
+ {
+ outroms.RemoveAt(pos);
+ outroms.Insert(pos, savedHash);
+ }
+ }
+ else
+ {
+ outroms.Add(hash);
+ }
+ }
+
+ // Then return the result
+ return outroms;
+ }
+
+ /*
+ ///
+ /// List all duplicates found in a DAT based on a rom
+ ///
+ /// Hash to use as a base
+ /// DAT to match against
+ /// Logger object for console and/or file output
+ /// True to remove matched roms from the input, false otherwise (default)
+ /// List of matched HashData objects
+ public static List GetDuplicates(HashData lastrom, DatData datdata, Logger logger, bool remove = false)
+ {
+ List output = new List();
+
+ // Check for an empty rom list first
+ if (datdata.Hashes == null || datdata.Hashes.Count == 0)
+ {
+ return output;
+ }
+
+ // Try to find duplicates
+ for (int i = 0; i < datdata.Hashes.Count; i++)
+ {
+
+ List roms = datdata.Files[key];
+ List left = new List();
+ foreach (Rom rom in roms)
+ {
+ if (IsDuplicate(rom, lastrom, logger))
+ {
+ output.Add(rom);
+ }
+ else
+ {
+ left.Add(rom);
+ }
+ }
+
+ // If we're in removal mode, replace the list with the new one
+ if (remove)
+ {
+ datdata.Files[key] = left;
+ }
+ }
+
+ return output;
+ }
+ */
+
+ ///
+ /// Determine if a file is a duplicate using partial matching logic
+ ///
+ /// Hash to check for duplicate status
+ /// Hash to use as a baseline
+ /// Logger object for console and/or file output
+ /// True if the hashes are duplicates, false otherwise
+ public static bool IsDuplicate(HashData hash, int hashIndex, HashData lastHash, int lastHashIndex, Logger logger)
+ {
+ bool dupefound = hash.Equals(lastHash);
+
+ // More wonderful SHA-1 logging that has to be done
+ if (hash.SHA1 == lastHash.SHA1 && hash.Size != lastHash.Size)
+ {
+ logger.User("SHA-1 mismatch - Hash: " + hash.SHA1);
+ }
+
+ return dupefound;
+ }
+
+ ///
+ /// Return the duplicate status of two hashes
+ ///
+ /// Current hash to check
+ /// Last hash to check against
+ /// Logger object for console and/or file output
+ /// The DupeType corresponding to the relationship between the two
+ public static DupeType GetDuplicateStatus(HashData hash, HashData lasthash, Logger logger)
+ {
+ DupeType output = DupeType.None;
+
+ /*
+ // If we don't have a duplicate at all, return none
+ if (!IsDuplicate(hash, lasthash, logger))
+ {
+ return output;
+ }
+ */
+
+ // If the duplicate is external already or should be, set it
+ if (lasthash.Roms[0].DupeType >= DupeType.ExternalHash || lasthash.Roms[0].Machine.SystemID != hash.Roms[0].Machine.SystemID || lasthash.Roms[0].Machine.SourceID != hash.Roms[0].Machine.SourceID)
+ {
+ if (lasthash.Roms[0].Machine.Name == hash.Roms[0].Machine.Name && lasthash.Roms[0].Name == hash.Roms[0].Name)
+ {
+ output = DupeType.ExternalAll;
+ }
+ else
+ {
+ output = DupeType.ExternalHash;
+ }
+ }
+
+ // Otherwise, it's considered an internal dupe
+ else
+ {
+ if (lasthash.Roms[0].Machine.Name == hash.Roms[0].Machine.Name && lasthash.Roms[0].Name == hash.Roms[0].Name)
+ {
+ output = DupeType.InternalAll;
+ }
+ else
+ {
+ output = DupeType.InternalHash;
+ }
+ }
+
+ return output;
+ }
+
+ #endregion
+ }
+}