using System; using System.Collections.Generic; using System.Linq; using System.Web; using SabreTools.Helper.Data; namespace SabreTools.Helper.Dats { public partial class DatFile { #region Instance Methods #region Bucketing [MODULAR DONE] /// /// Take the arbitrarily sorted Files Dictionary and convert to one sorted by CRC /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise public void BucketByCRC(bool mergeroms, Logger logger, bool output = true) { // If we already have the right sorting, trust it if (_sortedBy == SortedBy.CRC) { return; } // Set the sorted type _sortedBy = SortedBy.CRC; SortedDictionary> sortable = new SortedDictionary>(); long count = 0; logger.User("Organizing " + (mergeroms ? "and merging " : "") + "roms by CRC"); // Process each all of the roms List keys = Keys.ToList(); foreach (string key in keys) { List roms = this[key]; // If we're merging the roms, do so if (mergeroms) { roms = DatItem.Merge(roms, logger); } // Now add each of the roms to their respective games foreach (DatItem rom in roms) { count++; string newkey = (rom.Type == ItemType.Rom ? ((Rom)rom).CRC : Constants.CRCZero); if (!sortable.ContainsKey(newkey)) { sortable.Add(newkey, new List()); } sortable[newkey].Add(rom); } } // Now go through and sort all of the lists keys = sortable.Keys.ToList(); foreach (string key in keys) { List sortedlist = sortable[key]; DatItem.Sort(ref sortedlist, false); sortable[key] = sortedlist; } // Output the count if told to if (output) { logger.User("A total of " + count + " file hashes will be written out to file"); } // Now assign the dictionary back _files = sortable; } /// /// Take the arbitrarily sorted Files Dictionary and convert to one sorted by Game /// /// True if roms should be deduped, false otherwise /// True if games should only be compared on game and file name, false if system and source are counted /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise /// True if the game should be lowercased (default), false otherwise public void BucketByGame(bool mergeroms, bool norename, Logger logger, bool output = true, bool lower = true) { // If we already have the right sorting, trust it if (_sortedBy == SortedBy.Game) { return; } // Set the sorted type _sortedBy = SortedBy.Game; SortedDictionary> sortable = new SortedDictionary>(); long count = 0; logger.User("Organizing " + (mergeroms ? "and merging " : "") + "roms by game"); // Process each all of the roms List keys = Keys.ToList(); foreach (string key in keys) { List roms = this[key]; // If we're merging the roms, do so if (mergeroms) { roms = DatItem.Merge(roms, logger); } // Now add each of the roms to their respective games foreach (DatItem rom in roms) { count++; string newkey = (norename ? "" : rom.SystemID.ToString().PadLeft(10, '0') + "-" + rom.SourceID.ToString().PadLeft(10, '0') + "-") + (String.IsNullOrEmpty(rom.Machine.Name) ? "Default" : rom.Machine.Name); if (lower) { newkey = newkey.ToLowerInvariant(); } newkey = HttpUtility.HtmlEncode(newkey); if (!sortable.ContainsKey(newkey)) { sortable.Add(newkey, new List()); } sortable[newkey].Add(rom); } } // Now go through and sort all of the lists keys = sortable.Keys.ToList(); foreach (string key in keys) { List sortedlist = sortable[key]; DatItem.Sort(ref sortedlist, norename); sortable[key] = sortedlist; } // Output the count if told to if (output) { logger.User("A total of " + count + " file hashes will be written out to file"); } // Now assign the dictionary back _files = sortable; } /// /// Take the arbitrarily sorted Files Dictionary and convert to one sorted by MD5 /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise public void BucketByMD5(bool mergeroms, Logger logger, bool output = true) { // If we already have the right sorting, trust it if (_sortedBy == SortedBy.MD5) { return; } // Set the sorted type _sortedBy = SortedBy.MD5; SortedDictionary> sortable = new SortedDictionary>(); long count = 0; logger.User("Organizing " + (mergeroms ? "and merging " : "") + "roms by MD5"); // Process each all of the roms List keys = Keys.ToList(); foreach (string key in keys) { List roms = this[key]; // If we're merging the roms, do so if (mergeroms) { roms = DatItem.Merge(roms, logger); } // Now add each of the roms to their respective games foreach (DatItem rom in roms) { count++; string newkey = (rom.Type == ItemType.Rom ? ((Rom)rom).MD5 : (rom.Type == ItemType.Disk ? ((Disk)rom).MD5 : Constants.MD5Zero)); if (!sortable.ContainsKey(newkey)) { sortable.Add(newkey, new List()); } sortable[newkey].Add(rom); } } // Now go through and sort all of the lists keys = sortable.Keys.ToList(); foreach (string key in keys) { List sortedlist = sortable[key]; DatItem.Sort(ref sortedlist, false); sortable[key] = sortedlist; } // Output the count if told to if (output) { logger.User("A total of " + count + " file hashes will be written out to file"); } // Now assign the dictionary back _files = sortable; } /// /// Take the arbitrarily sorted Files Dictionary and convert to one sorted by SHA1 /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise public void BucketBySHA1(bool mergeroms, Logger logger, bool output = true) { // If we already have the right sorting, trust it if (_sortedBy == SortedBy.SHA1) { return; } // Set the sorted type _sortedBy = SortedBy.SHA1; SortedDictionary> sortable = new SortedDictionary>(); long count = 0; logger.User("Organizing " + (mergeroms ? "and merging " : "") + "roms by SHA-1"); // Process each all of the roms List keys = Keys.ToList(); foreach (string key in keys) { List roms = this[key]; // If we're merging the roms, do so if (mergeroms) { roms = DatItem.Merge(roms, logger); } // Now add each of the roms to their respective games foreach (DatItem rom in roms) { count++; string newkey = (rom.Type == ItemType.Rom ? ((Rom)rom).SHA1 : (rom.Type == ItemType.Disk ? ((Disk)rom).SHA1 : Constants.MD5Zero)); if (!sortable.ContainsKey(newkey)) { sortable.Add(newkey, new List()); } sortable[newkey].Add(rom); } } // Now go through and sort all of the lists keys = sortable.Keys.ToList(); foreach (string key in keys) { List sortedlist = sortable[key]; DatItem.Sort(ref sortedlist, false); sortable[key] = sortedlist; } // Output the count if told to if (output) { logger.User("A total of " + count + " file hashes will be written out to file"); } // Now assign the dictionary back _files = sortable; } /// /// Take the arbitrarily sorted Files Dictionary and convert to one sorted by Size /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise public void BucketBySize(bool mergeroms, Logger logger, bool output = true) { // If we already have the right sorting, trust it if (_sortedBy == SortedBy.Size) { return; } // Set the sorted type _sortedBy = SortedBy.Size; SortedDictionary> sortable = new SortedDictionary>(); long count = 0; logger.User("Organizing " + (mergeroms ? "and merging " : "") + "roms by size"); // Process each all of the roms List keys = Keys.ToList(); foreach (string key in keys) { List roms = this[key]; // If we're merging the roms, do so if (mergeroms) { roms = DatItem.Merge(roms, logger); } // Now add each of the roms to their respective games foreach (DatItem rom in roms) { count++; string newkey = (rom.Type == ItemType.Rom ? ((Rom)rom).Size.ToString() : "-1"); if (!sortable.ContainsKey(newkey)) { sortable.Add(newkey, new List()); } sortable[newkey].Add(rom); } } // Now go through and sort all of the lists keys = sortable.Keys.ToList(); foreach (string key in keys) { List sortedlist = sortable[key]; DatItem.Sort(ref sortedlist, false); sortable[key] = sortedlist; } // Output the count if told to if (output) { logger.User("A total of " + count + " file hashes will be written out to file"); } // Now assign the dictionary back _files = sortable; } /// /// Use cloneof tags to create non-merged sets and remove the tags plus using the device_ref tags to get full sets /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise /// TODO: This is not actually complete currently as it copies the code from CreateMergedSets public void CreateFullyNonMergedSets(bool mergeroms, Logger logger, bool output = true) { // First, we try to add all device items first /* Here, we require that device_ref tags got read first, and then we can add them accordingly. As of right now, those tags are NOT being read into the machine. We'd have to do this in order to get this working correctly. At least this is a good placeholder for now. */ // For sake of ease, the first thing we want to do is sort by game BucketByGame(mergeroms, true, logger, output); _sortedBy = SortedBy.Default; // Now we want to loop through all of the games and set the correct information List games = Keys.ToList(); foreach (string game in games) { // Determine if the game has a parent or not string parent = null; if (!String.IsNullOrEmpty(this[game][0].Machine.CloneOf)) { parent = this[game][0].Machine.CloneOf; } else if (!String.IsNullOrEmpty(this[game][0].Machine.RomOf)) { parent = this[game][0].Machine.RomOf; } // If there is no parent, then we continue if (String.IsNullOrEmpty(parent)) { continue; } // If the parent doesn't exist, then we continue and remove if (this[parent].Count == 0) { List curitems = this[game]; foreach (DatItem item in curitems) { item.Machine.CloneOf = null; item.Machine.RomOf = null; } continue; } // Otherwise, copy the items from the parent to the current game Machine currentMachine = this[game][0].Machine; List items = this[parent]; foreach (DatItem item in items) { // Figure out the type of the item and add it accordingly switch (item.Type) { case ItemType.Archive: Archive archive = ((Archive)item).Clone() as Archive; archive.Machine = currentMachine; if (!this[game].Contains(archive)) { this[game].Add(archive); } break; case ItemType.BiosSet: BiosSet biosSet = ((BiosSet)item).Clone() as BiosSet; biosSet.Machine = currentMachine; if (!this[game].Contains(biosSet)) { this[game].Add(biosSet); } break; case ItemType.Disk: Disk disk = ((Disk)item).Clone() as Disk; disk.Machine = currentMachine; if (!this[game].Contains(disk)) { this[game].Add(disk); } break; case ItemType.Release: Release release = ((Release)item).Clone() as Release; release.Machine = currentMachine; if (!this[game].Contains(release)) { this[game].Add(release); } break; case ItemType.Rom: Rom rom = ((Rom)item).Clone() as Rom; rom.Machine = currentMachine; if (!this[game].Contains(rom)) { this[game].Add(rom); } break; case ItemType.Sample: Sample sample = ((Sample)item).Clone() as Sample; sample.Machine = currentMachine; if (!this[game].Contains(sample)) { this[game].Add(sample); } break; } } // Finally, remove the romof and cloneof tags so it's not picked up by the manager items = this[game]; foreach (DatItem item in items) { item.Machine.CloneOf = null; item.Machine.RomOf = null; } } } /// /// Use cloneof tags to create merged sets and remove the tags /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise public void CreateMergedSets(bool mergeroms, Logger logger, bool output = true) { // For sake of ease, the first thing we want to do is sort by game BucketByGame(mergeroms, true, logger, output); _sortedBy = SortedBy.Default; // Now we want to loop through all of the games and set the correct information List games = Keys.ToList(); foreach (string game in games) { // Determine if the game has a parent or not string parent = null; if (!String.IsNullOrEmpty(this[game][0].Machine.CloneOf)) { parent = this[game][0].Machine.CloneOf; } // If there is no parent, then we continue if (String.IsNullOrEmpty(parent)) { continue; } // Otherwise, move the items from the current game to a subfolder of the parent game Machine parentMachine = this[parent].Count == 0 ? new Machine { Name = parent, Description = parent } : this[parent][0].Machine; List items = this[game]; foreach (DatItem item in items) { if (!this[parent].Contains(item)) { item.Name = item.Machine.Name + "\\" + item.Name; item.Machine = parentMachine; this[parent].Add(item); } } // Finally, remove the old game so it's not picked up by the writer Remove(game); } } /// /// Use cloneof tags to create non-merged sets and remove the tags /// /// True if roms should be deduped, false otherwise /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise public void CreateNonMergedSets(bool mergeroms, Logger logger, bool output = true) { // For sake of ease, the first thing we want to do is sort by game BucketByGame(mergeroms, true, logger, output); _sortedBy = SortedBy.Default; // Now we want to loop through all of the games and set the correct information List games = Keys.ToList(); foreach (string game in games) { // Determine if the game has a parent or not string parent = null; if (!String.IsNullOrEmpty(this[game][0].Machine.CloneOf)) { parent = this[game][0].Machine.CloneOf; } // If there is no parent, then we continue if (String.IsNullOrEmpty(parent)) { continue; } // If the parent doesn't exist, then we continue and remove if (this[parent].Count == 0) { List curitems = this[game]; foreach (DatItem item in curitems) { item.Machine.CloneOf = null; item.Machine.RomOf = null; } continue; } // Otherwise, copy the items from the parent to the current game Machine currentMachine = this[game][0].Machine; List items = this[parent]; foreach (DatItem item in items) { // Figure out the type of the item and add it accordingly switch (item.Type) { case ItemType.Archive: Archive archive = ((Archive)item).Clone() as Archive; archive.Machine = currentMachine; if (!this[game].Contains(archive)) { this[game].Add(archive); } break; case ItemType.BiosSet: BiosSet biosSet = ((BiosSet)item).Clone() as BiosSet; biosSet.Machine = currentMachine; if (!this[game].Contains(biosSet)) { this[game].Add(biosSet); } break; case ItemType.Disk: Disk disk = ((Disk)item).Clone() as Disk; disk.Machine = currentMachine; if (!this[game].Contains(disk)) { this[game].Add(disk); } break; case ItemType.Release: Release release = ((Release)item).Clone() as Release; release.Machine = currentMachine; if (!this[game].Contains(release)) { this[game].Add(release); } break; case ItemType.Rom: Rom rom = ((Rom)item).Clone() as Rom; rom.Machine = currentMachine; if (!this[game].Contains(rom)) { this[game].Add(rom); } break; case ItemType.Sample: Sample sample = ((Sample)item).Clone() as Sample; sample.Machine = currentMachine; if (!this[game].Contains(sample)) { this[game].Add(sample); } break; } } // Finally, remove the romof and cloneof tags so it's not picked up by the manager items = this[game]; foreach (DatItem item in items) { item.Machine.CloneOf = null; item.Machine.RomOf = null; } } } #endregion #endregion // Instance Methods #region Static Methods #region Bucketing [MODULAR DONE] /// /// Take an arbitrarily ordered List and return a Dictionary sorted by Game /// /// Input unsorted list /// True if roms should be deduped, false otherwise /// True if games should only be compared on game and file name, false if system and source are counted /// Logger object for file and console output /// True if the number of hashes counted is to be output (default), false otherwise /// SortedDictionary bucketed by game name public static SortedDictionary> BucketListByGame(List list, bool mergeroms, bool norename, Logger logger, bool output = true) { logger.User("Organizing " + (mergeroms ? "and merging " : "") + "roms for output"); SortedDictionary> sortable = new SortedDictionary>(); long count = 0; // If we have a null dict or an empty one, output a new dictionary if (list == null || list.Count == 0) { return sortable; } // If we're merging the roms, do so if (mergeroms) { list = DatItem.Merge(list, logger); } // Now add each of the roms to their respective games foreach (DatItem rom in list) { if (rom == null) { continue; } count++; string newkey = (norename ? "" : rom.SystemID.ToString().PadLeft(10, '0') + "-" + rom.SourceID.ToString().PadLeft(10, '0') + "-") + (rom.Machine == null || String.IsNullOrEmpty(rom.Machine.Name) ? "Default" : rom.Machine.Name.ToLowerInvariant()); newkey = HttpUtility.HtmlEncode(newkey); if (!sortable.ContainsKey(newkey)) { sortable.Add(newkey, new List()); } sortable[newkey].Add(rom); } return sortable; } #endregion #endregion // Static Methods } }