using Mono.Data.Sqlite; using SabreTools.Helper; using System; using System.Collections.Generic; using System.IO; namespace SabreTools { /// /// Generate a DAT from the data in the database /// public class Generate : IGenerate { // Private instance variables private string _systems; private string _sources; private string _outDir; private string _connectionString; private bool _norename; private bool _old; // Private required variables private Logger _logger; /// /// Initialize a Generate object with the given information /// /// Comma-separated list of systems to be included in the DAT (blank means all) /// Comma-separated list of sources to be included in the DAT (blank means all) /// The output folder where the generated DAT will be put; blank means the current directory /// Connection string for SQLite /// Logger object for file or console output /// True if files should not be renamed with system and/or source in merged mode (default false) /// True if the output file should be in ClrMamePro format (default false) public Generate(string systems, string sources, string outDir, string connectionString, Logger logger, bool norename = false, bool old = false) { _systems = systems; _sources = sources; _connectionString = connectionString; _norename = norename; _old = old; _logger = logger; // Take care of special outfolder cases _outDir = (outDir == "" ? Environment.CurrentDirectory + Path.DirectorySeparatorChar : (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString()) ? outDir + Path.DirectorySeparatorChar : outDir) ); if (_outDir != "" && !Directory.Exists(_outDir)) { Directory.CreateDirectory(_outDir); } } /// /// Generate a DAT file that is represented by the data in the Generate object. /// /// True if the file could be created, false otherwise public bool Export() { // Check to see if the source is an import-only. If so, tell the user and exit int id = 0; if (_sources != "" && Int32.TryParse(_sources, out id) && id <= 14) { _logger.Warning("This source (" + id + ") is import-only so a DAT cannot be created. We apologize for the inconvenience."); return false; } // Get the system name, if applicable string systemname = ""; if (_systems != "") { string query = "SELECT manufacturer, system FROM systems WHERE id in (" + _systems + ")"; //string query = "SELECT manufacturer, name FROM system WHERE id in (" + _systems + ")"; using (SqliteConnection dbc = new SqliteConnection(_connectionString)) { dbc.Open(); using (SqliteCommand slc = new SqliteCommand(query, dbc)) { using (SqliteDataReader sldr = slc.ExecuteReader()) { // If there are no games for this combination, return nothing if (!sldr.HasRows) { _logger.Error("No system could be found with id in \"" + _systems + "\". Please check and try again."); return false; } // Retrieve and build the system name from all retrieved int tempsize = 0; while (sldr.Read() && tempsize < 3) { systemname += (tempsize == 0 ? sldr.GetString(0) + " - " + sldr.GetString(1) : "; " + sldr.GetString(0) + " - " + sldr.GetString(1)); tempsize++; } // If there are more than 3 systems, just put "etc." on the end if (sldr.Read()) { systemname += "; etc."; } } } } } else { systemname = "ALL"; } string sourcename = ""; if (_sources != "") { string query = "SELECT name FROM sources WHERE id in (" + _sources + ")"; //string query = "SELECT name FROM source WHERE id in (" + _sources + ")"; using (SqliteConnection dbc = new SqliteConnection(_connectionString)) { dbc.Open(); using (SqliteCommand slc = new SqliteCommand(query, dbc)) { using (SqliteDataReader sldr = slc.ExecuteReader()) { // If there are no games for this combination, return nothing if (!sldr.HasRows) { _logger.Error("No source could be found with id in \"" + _sources + "\". Please check and try again."); return false; } // Retrieve and build the source name from all retrieved int tempsize = 0; while (sldr.Read() && tempsize < 3) { sourcename += (tempsize == 0 ? sldr.GetString(0) : "; " + sldr.GetString(0)); tempsize++; } // If there are more than 3 systems, just put "etc." on the end if (sldr.Read()) { sourcename += "; etc."; } } } } } else { sourcename = "Merged"; } // Retrieve the list of processed roms Dictionary> dict = ProcessRoms(); // If the output is null, nothing was found so return false if (dict.Count == 0) { return false; } // Create a name for the file based on the retrieved information string version = DateTime.Now.ToString("yyyyMMddHHmmss"); string intname = systemname + " (" + sourcename + ")"; string datname = systemname + " (" + sourcename + " " + version + ")"; DatFile datdata = new DatFile { Name = intname, Description = datname, Version = version, Date = version, Category = "The Wizard of DATz", Author = "The Wizard of DATz", ForcePacking = ForcePacking.None, OutputFormat = (_old ? OutputFormat.ClrMamePro : OutputFormat.Xml), Files = dict, }; return DatFile.WriteDatfile(datdata, _outDir, _logger); } /// /// Preprocess the rom data that is to be included in the outputted DAT /// /// A List of Rom objects containing all information about the files public Dictionary> ProcessRoms() { Dictionary> roms = new Dictionary>(); // Check if we have listed sources or systems bool sysmerged = (_systems == "" || _systems.Split(',').Length > 1); bool srcmerged = (_sources == "" || _sources.Split(',').Length > 1); bool merged = sysmerged || srcmerged; // BEGIN COMMENT string query = @" SELECT DISTINCT systems.manufacturer AS manufacturer, systems.system AS system, systems.id AS systemid, sources.name AS source, sources.url AS url, sources.id AS sourceid, games.name AS game, files.name AS name, files.type AS type, checksums.size AS size, checksums.crc AS crc, checksums.md5 AS md5, checksums.sha1 AS sha1, files.lastupdated AS lastupdated FROM systems JOIN games ON systems.id=games.system JOIN sources ON games.source=sources.id JOIN files ON games.id=files.setid JOIN checksums ON files.id=checksums.file" + (_systems != "" || _sources != "" ? "\nWHERE" : "") + (_sources != "" ? " sources.id in (" + _sources + ")" : "") + (_systems != "" && _sources != "" ? " AND" : "") + (_systems != "" ? " systems.id in (" + _systems + ")" : "") + "\nORDER BY " + (merged ? "checksums.size, checksums.crc, systems.id, sources.id, files.lastupdated DESC, checksums.md5, checksums.sha1" : "systems.id, sources.id, games.name, files.name"); using (SqliteConnection dbc = new SqliteConnection(_connectionString)) { dbc.Open(); using (SqliteCommand slc = new SqliteCommand(query, dbc)) { using (SqliteDataReader sldr = slc.ExecuteReader()) { // If there are no games for this combination, return nothing if (!sldr.HasRows) { _logger.Error("No games could be found with those inputs. Please check and try again."); return null; } // Retrieve and process the roms for merging while (sldr.Read()) { DatItem temp; string key = ""; switch (sldr.GetString(8).ToLowerInvariant()) { case "disk": temp = new Disk(sldr.GetString(7), sldr.GetString(11), sldr.GetString(12), false, sldr.GetString(13), sldr.GetString(6), sldr.GetString(6), null, sldr.GetString(0), null, null, null, null, false, null, null, sldr.GetInt32(2), sldr.GetString(1), sldr.GetInt32(5), sldr.GetString(3)); key = ((Disk)temp).MD5; break; case "rom": default: temp = new Rom(sldr.GetString(7), sldr.GetInt64(9), sldr.GetString(10), sldr.GetString(11), sldr.GetString(12), false, sldr.GetString(13), sldr.GetString(6), null, sldr.GetString(6), null, sldr.GetString(0), null, null, null, null, false, null, null, sldr.GetInt32(2), sldr.GetString(1), sldr.GetInt32(5), sldr.GetString(3)); key = ((Rom)temp).Size + "-" + ((Rom)temp).CRC; break; } // Rename the game associated if it's still valid and we allow renames if (merged && !_norename) { temp.MachineName = temp.MachineName + (sysmerged ? " [" + temp.Manufacturer + " - " + temp.System + "]" : "") + (srcmerged ? " [" + temp.Source + "]" : ""); } if (roms.ContainsKey(key)) { roms[key].Add(temp); } else { List templist = new List(); templist.Add(temp); roms.Add(key, templist); } } } } } // If we're in a merged mode, merge and then resort by the correct parameters if (merged) { foreach (string key in roms.Keys) { roms[key] = DatItem.Merge(roms[key], _logger); } } // END COMMENT /* // This block would replace the whole block above between BEGIN COMMENT and END COMMENT string query = @" SELECT hash.id AS id, hash.size AS size, hash.crc AS crc, hash.md5 AS md5, hash.sha1 AS sha1, a.key AS key, a.value AS value, source.id, source.name, source.url, system.id, system.manufacturer, system.name FROM hash JOIN hashdata a ON hash.id=a.hashid JOIN hashdata b ON a.hashid=b.hashid JOIN gamesystem ON b.value=gamesystem.game JOIN gamesource ON b.value=gamesource.game JOIN system ON gamesystem.systemid=system.id JOIN source ON gamesource.sourceid=source.id" + (_systems != "" || _sources != "" ? "\nWHERE" : "") + (_sources != "" ? " source.id in (" + _sources + ")" : "") + (_systems != "" && _sources != "" ? " AND" : "") + (_systems != "" ? " system.id in (" + _systems + ")" : "") + "\nORDER BY hash.id"; using (SqliteConnection dbc = new SqliteConnection(_connectionString)) { dbc.Open(); using (SqliteCommand slc = new SqliteCommand(query, dbc)) { using (SqliteDataReader sldr = slc.ExecuteReader()) { // If there are no games for this combination, return nothing if (!sldr.HasRows) { _logger.Error("No games could be found with those inputs. Please check and try again."); return null; } // Retrieve and process the roms for merging int systemid = -1, sourceid = -1; long lastid = -1, size = -1; string name = "", game = "", type = "", manufacturer = "", system = "", source = "", url = "", crc = "", md5 = "", sha1 = ""; while (sldr.Read()) { // If the hash is different than the last if (lastid != -1 && sldr.GetInt64(0) != lastid) { Rom temp = new Rom { Manufacturer = manufacturer, System = system, SystemID = systemid, Source = source, URL = url, SourceID = sourceid, Game = game, Name = name, Type = type, Size = size, CRC = crc, MD5 = md5, SHA1 = sha1, }; // Rename the game associated if it's still valid and we allow renames if (merged && !_norename) { temp.Machine = temp.Machine + (sysmerged ? " [" + temp.Manufacturer + " - " + temp.System + "]" : "") + (srcmerged ? " [" + temp.Source + "]" : ""); } string key = temp.Size + "-" + temp.CRC; if (roms.ContainsKey(key)) { roms[key].Add(temp); } else { List templist = new List(); templist.Add(temp); roms.Add(key, templist); } // Reset the variables game = ""; name = ""; type = ""; } // Get all of the current ROM information manufacturer = sldr.GetString(11); system = sldr.GetString(12); systemid = sldr.GetInt32(10); source = sldr.GetString(8); url = sldr.GetString(9); sourceid = sldr.GetInt32(7); size = sldr.GetInt64(1); crc = sldr.GetString(2); md5 = sldr.GetString(3); sha1 = sldr.GetString(4); switch (sldr.GetString(5)) { case "game": game = sldr.GetString(6); break; case "name": name = sldr.GetString(6); break; case "type": type = sldr.GetString(6); break; } lastid = sldr.GetInt64(0); } } } } // If we're in a merged mode, merge if (merged) { foreach (string key in roms.Keys) { roms[key] = RomManipulation.Merge(roms[key]); } } */ /* // THIS CODE SHOULD BE PUT IN WriteToDatFromDict // Now check rename within games string lastname = "", lastgame = ""; for (int i = 0; i < roms.Count; i++) { Rom rom = roms[i]; // Now relable any roms that have the same name inside of the same game bool samename = false, samegame = false; if (rom.Name != "") { samename = (lastname == rom.Name); } if (rom.Machine != "") { samegame = (lastgame == rom.Machine); } lastname = rom.Name; lastgame = rom.Machine; // If the name and set are the same, rename it with whatever is different if (samename && samegame) { rom.Name = Regex.Replace(rom.Name, @"^(.*)(\..*)", "$1(" + (rom.CRC != "" ? rom.CRC : (rom.MD5 != "" ? rom.MD5 : (rom.SHA1 != "" ? rom.SHA1 : "Alt"))) + ")$2"); } // Assign back just in case roms[i] = rom; } */ return roms; } } }