using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace SabreTools.Helper
{
public class RomTools
{
#region Rom-based sorting and merging
///
/// Determine if a rom should be included based on filters
///
/// User supplied Rom to check
/// Name of the game to match (can use asterisk-partials)
/// Name of the rom to match (can use asterisk-partials)
/// Type of the rom to match
/// Find roms greater than or equal to this size
/// Find roms less than or equal to this size
/// Find roms equal to this size
/// CRC of the rom to match (can use asterisk-partials)
/// MD5 of the rom to match (can use asterisk-partials)
/// SHA-1 of the rom to match (can use asterisk-partials)
/// Select roms with nodump status as follows: null (match all), true (match Nodump only), false (exclude Nodump)
/// Logging object for console and file output
/// Returns true if it should be included, false otherwise
public static bool Filter(Rom romdata, string gamename, string romname, string romtype, long sgt,
long slt, long seq, string crc, string md5, string sha1, bool? nodump, Logger logger)
{
// Filter on nodump status
if (nodump == true && !romdata.Nodump)
{
return false;
}
if (nodump == false && romdata.Nodump)
{
return false;
}
// Filter on game name
if (!String.IsNullOrEmpty(gamename))
{
if (gamename.StartsWith("*") && gamename.EndsWith("*"))
{
if (!romdata.Machine.Name.ToLowerInvariant().Contains(gamename.ToLowerInvariant().Replace("*", "")))
{
return false;
}
}
else if (gamename.StartsWith("*"))
{
if (!romdata.Machine.Name.EndsWith(gamename.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else if (gamename.EndsWith("*"))
{
if (!romdata.Machine.Name.StartsWith(gamename.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else
{
if (!String.Equals(romdata.Machine.Name, gamename, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
}
// Filter on rom name
if (!String.IsNullOrEmpty(romname))
{
if (romname.StartsWith("*") && romname.EndsWith("*"))
{
if (!romdata.Name.ToLowerInvariant().Contains(romname.ToLowerInvariant().Replace("*", "")))
{
return false;
}
}
else if (romname.StartsWith("*"))
{
if (!romdata.Name.EndsWith(romname.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else if (romname.EndsWith("*"))
{
if (!romdata.Name.StartsWith(romname.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else
{
if (!String.Equals(romdata.Name, romname, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
}
// Filter on rom type
if (String.IsNullOrEmpty(romtype) && romdata.Type != ItemType.Rom && romdata.Type != ItemType.Disk)
{
return false;
}
if (!String.IsNullOrEmpty(romtype) && !String.Equals(romdata.Type.ToString(), romtype, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
// Filter on rom size
if (seq != -1 && romdata.HashData.Size != seq)
{
return false;
}
else
{
if (sgt != -1 && romdata.HashData.Size < sgt)
{
return false;
}
if (slt != -1 && romdata.HashData.Size > slt)
{
return false;
}
}
// Filter on crc
if (!String.IsNullOrEmpty(crc))
{
if (crc.StartsWith("*") && crc.EndsWith("*"))
{
if (!romdata.HashData.CRC.ToLowerInvariant().Contains(crc.ToLowerInvariant().Replace("*", "")))
{
return false;
}
}
else if (crc.StartsWith("*"))
{
if (!romdata.HashData.CRC.EndsWith(crc.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else if (crc.EndsWith("*"))
{
if (!romdata.HashData.CRC.StartsWith(crc.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else
{
if (!String.Equals(romdata.HashData.CRC, crc, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
}
// Filter on md5
if (!String.IsNullOrEmpty(md5))
{
if (md5.StartsWith("*") && md5.EndsWith("*"))
{
if (!romdata.HashData.MD5.ToLowerInvariant().Contains(md5.ToLowerInvariant().Replace("*", "")))
{
return false;
}
}
else if (md5.StartsWith("*"))
{
if (!romdata.HashData.MD5.EndsWith(md5.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else if (md5.EndsWith("*"))
{
if (!romdata.HashData.MD5.StartsWith(md5.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else
{
if (!String.Equals(romdata.HashData.MD5, md5, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
}
// Filter on sha1
if (!String.IsNullOrEmpty(sha1))
{
if (sha1.StartsWith("*") && sha1.EndsWith("*"))
{
if (!romdata.HashData.SHA1.ToLowerInvariant().Contains(sha1.ToLowerInvariant().Replace("*", "")))
{
return false;
}
}
else if (sha1.StartsWith("*"))
{
if (!romdata.HashData.SHA1.EndsWith(sha1.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else if (sha1.EndsWith("*"))
{
if (!romdata.HashData.SHA1.StartsWith(sha1.Replace("*", ""), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
else
{
if (!String.Equals(romdata.HashData.SHA1, sha1, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
}
return true;
}
///
/// Merge an arbitrary set of ROMs based on the supplied information
///
/// List of RomData objects representing the roms to be merged
/// Logger object for console and/or file output
/// A List of RomData objects representing the merged roms
public static List Merge(List inroms, Logger logger)
{
// Check for null or blank roms first
if (inroms == null || inroms.Count == 0)
{
return new List();
}
// Create output list
List outroms = new List();
// Then deduplicate them by checking to see if data matches previous saved roms
foreach (Rom rom in inroms)
{
// If it's a nodump, add and skip
if (rom.Nodump)
{
outroms.Add(rom);
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;
Rom savedrom = new Rom();
int pos = -1;
for (int i = 0; i < outroms.Count; i++)
{
Rom lastrom = outroms[i];
// Get the duplicate status
dupetype = GetDuplicateStatus(rom, lastrom, logger);
// If it's a duplicate, skip adding it to the output but add any missing information
if (dupetype != DupeType.None)
{
savedrom = lastrom;
pos = i;
savedrom.HashData.CRC = (String.IsNullOrEmpty(savedrom.HashData.CRC) && !String.IsNullOrEmpty(rom.HashData.CRC) ? rom.HashData.CRC : savedrom.HashData.CRC);
savedrom.HashData.MD5 = (String.IsNullOrEmpty(savedrom.HashData.MD5) && !String.IsNullOrEmpty(rom.HashData.MD5) ? rom.HashData.MD5 : savedrom.HashData.MD5);
savedrom.HashData.SHA1 = (String.IsNullOrEmpty(savedrom.HashData.SHA1) && !String.IsNullOrEmpty(rom.HashData.SHA1) ? rom.HashData.SHA1 : savedrom.HashData.SHA1);
savedrom.Dupe = dupetype;
// If the current system has a lower ID than the previous, set the system accordingly
if (rom.Metadata.SystemID < savedrom.Metadata.SystemID)
{
savedrom.Metadata.SystemID = rom.Metadata.SystemID;
savedrom.Metadata.System = rom.Metadata.System;
savedrom.Machine.Name = rom.Machine.Name;
savedrom.Name = rom.Name;
}
// If the current source has a lower ID than the previous, set the source accordingly
if (rom.Metadata.SourceID < savedrom.Metadata.SourceID)
{
savedrom.Metadata.SourceID = rom.Metadata.SourceID;
savedrom.Metadata.Source = rom.Metadata.Source;
savedrom.Machine.Name = rom.Machine.Name;
savedrom.Name = rom.Name;
}
break;
}
}
// If no duplicate is found, add it to the list
if (dupetype == DupeType.None)
{
outroms.Add(rom);
}
// Otherwise, if a new rom information is found, add that
else
{
outroms.RemoveAt(pos);
outroms.Insert(pos, savedrom);
}
}
else
{
outroms.Add(rom);
}
}
// Then return the result
return outroms;
}
///
/// List all duplicates found in a DAT based on a rom
///
/// Rom 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 RomData objects
public static List GetDuplicates(Rom lastrom, Dat datdata, Logger logger, bool remove = false)
{
List output = new List();
// Check for an empty rom list first
if (datdata.Files == null || datdata.Files.Count == 0)
{
return output;
}
// Try to find duplicates
List keys = datdata.Files.Keys.ToList();
foreach (string key in keys)
{
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
///
/// Rom to check for duplicate status
/// Rom to use as a baseline
/// Logger object for console and/or file output
/// True if the roms are duplicates, false otherwise
public static bool IsDuplicate(Rom rom, Rom lastrom, Logger logger)
{
bool dupefound = rom.Equals(lastrom);
// More wonderful SHA-1 logging that has to be done
if (rom.HashData.SHA1 == lastrom.HashData.SHA1 && rom.HashData.Size != lastrom.HashData.Size)
{
logger.User("SHA-1 mismatch - Hash: " + rom.HashData.SHA1);
}
return dupefound;
}
///
/// Return the duplicate status of two roms
///
/// Current rom to check
/// Last rom to check against
/// Logger object for console and/or file output
/// The DupeType corresponding to the relationship between the two
public static DupeType GetDuplicateStatus(Rom rom, Rom lastrom, Logger logger)
{
DupeType output = DupeType.None;
// If we don't have a duplicate at all, return none
if (!IsDuplicate(rom, lastrom, logger))
{
return output;
}
// If the duplicate is external already or should be, set it
if (lastrom.Dupe >= DupeType.ExternalHash || lastrom.Metadata.SystemID != rom.Metadata.SystemID || lastrom.Metadata.SourceID != rom.Metadata.SourceID)
{
if (lastrom.Machine.Name == rom.Machine.Name && lastrom.Name == rom.Name)
{
output = DupeType.ExternalAll;
}
else
{
output = DupeType.ExternalHash;
}
}
// Otherwise, it's considered an internal dupe
else
{
if (lastrom.Machine.Name == rom.Machine.Name && lastrom.Name == rom.Name)
{
output = DupeType.InternalAll;
}
else
{
output = DupeType.InternalHash;
}
}
return output;
}
///
/// Sort a list of RomData objects by SystemID, SourceID, Game, and Name (in order)
///
/// List of RomData objects representing the roms to be sorted
/// True if files are not renamed, false otherwise
/// True if it sorted correctly, false otherwise
public static bool Sort(ref List roms, bool norename)
{
try
{
roms.Sort(delegate (Rom x, Rom y)
{
if (x.Metadata.SystemID == y.Metadata.SystemID)
{
if (x.Metadata.SourceID == y.Metadata.SourceID)
{
if (x.Machine.Name == y.Machine.Name)
{
if ((x.Type == ItemType.Rom || x.Type == ItemType.Disk) && (y.Type == ItemType.Rom || y.Type == ItemType.Disk))
{
if (Path.GetDirectoryName(x.Name) == Path.GetDirectoryName(y.Name))
{
return Style.CompareNumeric(Path.GetFileName(x.Name), Path.GetFileName(y.Name));
}
return Style.CompareNumeric(Path.GetDirectoryName(x.Name), Path.GetDirectoryName(y.Name));
}
else if ((x.Type == ItemType.Rom || x.Type == ItemType.Disk) && (y.Type != ItemType.Rom && y.Type != ItemType.Disk))
{
return -1;
}
else if ((x.Type != ItemType.Rom && x.Type != ItemType.Disk) && (y.Type == ItemType.Rom || y.Type == ItemType.Disk))
{
return 1;
}
else
{
if (Path.GetDirectoryName(x.Name) == Path.GetDirectoryName(y.Name))
{
return Style.CompareNumeric(Path.GetFileName(x.Name), Path.GetFileName(y.Name));
}
return Style.CompareNumeric(Path.GetDirectoryName(x.Name), Path.GetDirectoryName(y.Name));
}
}
return Style.CompareNumeric(x.Machine.Name, y.Machine.Name);
}
return (norename ? Style.CompareNumeric(x.Machine.Name, y.Machine.Name) : x.Metadata.SourceID - y.Metadata.SourceID);
}
return (norename ? Style.CompareNumeric(x.Machine.Name, y.Machine.Name) : x.Metadata.SystemID - y.Metadata.SystemID);
});
return true;
}
catch (Exception)
{
// Absorb the error
return false;
}
}
#endregion
}
}