// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; namespace Spludlow.Mame { public class MameExport { /// /// Roms in parents ("merge" is not null) are omitted /// Roms in siblings can be duplicated, if not in parent /// public static string ExportMachineRom() { return ExportMachineRom(new string[0]); } public static string ExportMachineRom(string machineName) { return ExportMachineRom(new string[] { machineName }); } public static string ExportMachineRom(string[] machineNames) { MameConfiguration config = new MameConfiguration(); string targetDirectory = config.ExportDirectory; MameHashStores hashStores = new MameHashStores(); HashStore hashStore = hashStores.MachineRom; Spludlow.Data.IDAL database = MameDatabase.Machine(); HashSet machineNamesSet = new HashSet(machineNames); DataTable machineTable = database.Select("SELECT * FROM machine ORDER BY machine.name"); DataTable romTable = database.Select("SELECT * FROM rom WHERE sha1 IS NOT NULL ORDER BY rom.name"); DataTable reportTable = Spludlow.Data.TextTable.ReadText(new string[] { "Name Description Action CopyCount ParentCount DuplicateCount DontHaveCount", "String String String Int32 Int32 Int32 Int32", }); using (Spludlow.TempDirectory tempDir = new TempDirectory("")) { foreach (DataRow machineRow in machineTable.Rows) { string machine_name = (string)machineRow["name"]; if (machineNamesSet.Count > 0 && machineNamesSet.Contains(machine_name) == false) continue; string machine_description = (string)machineRow["description"]; long machine_id = (long)machineRow["machine_id"]; DataRow[] romRows = romTable.Select("machine_id = " + machine_id); if (romRows.Length == 0) { continue; } DataRow row = reportTable.Rows.Add(machine_name, machine_description, "", 0, 0, 0, 0); Dictionary nameHashes = new Dictionary(); int parentCount = 0; int duplicateCount = 0; int dontHaveCount = 0; foreach (DataRow romRow in romRows) { string sha1 = ((string)romRow["sha1"]).ToUpper(); if (romRow.IsNull("merge") == false) { ++parentCount; continue; } string rom_name = (string)romRow["name"]; if (nameHashes.ContainsKey(rom_name) == true) { if (nameHashes[rom_name] != sha1) throw new ApplicationException("ROM name sha1 mismatch: " + machine_name + "," + rom_name); ++duplicateCount; continue; } if (hashStore.Exists(sha1) == false) { ++dontHaveCount; continue; } nameHashes.Add(rom_name, sha1); } row["CopyCount"] = nameHashes.Count; row["ParentCount"] = parentCount; row["DuplicateCount"] = duplicateCount; row["DontHaveCount"] = dontHaveCount; if (dontHaveCount > 0) { row["Action"] = "Incomplete"; //continue; } if (nameHashes.Count == 0) { row["Action"] = "Nothing"; continue; } string archiveFilename = targetDirectory + @"\" + machine_name + ".zip"; if (File.Exists(archiveFilename) == true) { row["Action"] = "Exists"; continue; } string archiveDirectory = tempDir.Path + @"\" + machine_name; Directory.CreateDirectory(archiveDirectory); foreach (string rom_name in nameHashes.Keys) { string storeFilename = hashStore.Filename(nameHashes[rom_name]); string romFilename = archiveDirectory + @"\" + rom_name; File.Copy(storeFilename, romFilename); } Spludlow.Archive.Create(archiveFilename, archiveDirectory + @"\*", false); Directory.Delete(archiveDirectory, true); } } MameReport.Report("Exported Machine Roms", reportTable); return "Exported Machine Roms: " + reportTable.Rows.Count; } /// /// Disks in parents ("merge" is not null) are omitted /// Disks in siblings that are duplicated (already exported on this export) are omitted /// (machine will not be available but the disk is present in another machine) /// public static string ExportMachineDisk() { MameConfiguration config = new MameConfiguration(); string targetDirectory = config.ExportDirectory; return ExportMachineDisk(targetDirectory, new string[0]); } public static string ExportMachineDiskTarget(string targetDirectory) { return ExportMachineDisk(targetDirectory, new string[0]); } public static string ExportMachineDisk(string machineName) { MameConfiguration config = new MameConfiguration(); string targetDirectory = config.ExportDirectory; return ExportMachineDisk(targetDirectory, new string[] { machineName }); } public static string ExportMachineDisk(string targetDirectory, string[] machineNames) { MameHashStores hashStores = new MameHashStores(); HashStore hashStore = hashStores.MachineDisk; HashSet machineNamesSet = new HashSet(machineNames); DataTable table = MameDatabase.MachineDiskQuery("sha1 IS NOT NULL"); DataTable reportTable = Spludlow.Data.TextTable.ReadText(new string[] { "MachineName Description DiskName RomOf Merge SHA1 Action", "String String String String String String String", }); Dictionary nameHashes = new Dictionary(); foreach (DataRow row in table.Rows) { string machine_name = (string)row["machine_name"]; if (machineNamesSet.Count > 0 && machineNamesSet.Contains(machine_name) == false) continue; string machine_description = (string)row["description"]; string disk_name = (string)row["disk_name"]; string romof = ""; if (row.IsNull("romof") == false) romof = (string)row["romof"]; string merge = ""; if (row.IsNull("merge") == false) merge = (string)row["merge"]; string sha1 = ((string)row["sha1"]).ToUpper(); DataRow reportRow = reportTable.Rows.Add(machine_name, machine_description, disk_name, romof, merge, sha1, ""); if (merge != "") { reportRow["Action"] = "Parent"; continue; } string name = machine_name + @"\" + disk_name; if (nameHashes.ContainsKey(name) == true) throw new ApplicationException("Disk name duplicate: " + name); if (nameHashes.ContainsValue(sha1) == true) { reportRow["Action"] = "Sibling"; continue; } if (hashStore.Exists(sha1) == false) { reportRow["Action"] = "DontHave"; continue; } nameHashes.Add(name, sha1); string targetFilename = targetDirectory + @"\" + name + ".chd"; if (File.Exists(targetFilename) == true) { reportRow["Action"] = "Exists"; continue; } string directoryName = Path.GetDirectoryName(targetFilename); if (Directory.Exists(directoryName) == false) Directory.CreateDirectory(directoryName); string storeFilename = hashStore.Filename(sha1); File.Copy(storeFilename, targetFilename); } MameReport.Report("Exported Machine Disks", reportTable); return "Exported Machine Disks: " + reportTable.Rows.Count; } /// /// Check parents (cloneof) if SHA1 found Rom will not be duplicated /// public static string ExportSoftwareRom() { string[] softwareListNames = MameDatabase.SoftwareListsWithROMs(); return ExportSoftwareRom(softwareListNames); } public static string ExportSoftwareRom(string softwareListName) { return ExportSoftwareRom(new string[] { softwareListName }); } private static string _SoftwareQueryText = "SELECT software.* FROM softwarelist INNER JOIN software ON " + "softwarelist.softwarelist_Id = software.softwarelist_Id " + "WHERE (softwarelist.name = @SoftwareListName) ORDER BY software.name"; public static string ExportSoftwareRom(string[] softwareListNames) { MameConfiguration config = new MameConfiguration(); string targetDirectory = config.ExportDirectory; MameHashStores hashStores = new MameHashStores(); HashStore hashStore = hashStores.SoftwareRom; Spludlow.Data.IDAL database = MameDatabase.Software(); DataTable reportTable = Spludlow.Data.TextTable.ReadText(new string[] { "ListName SoftwareName Action CopyCount ParentCount DuplicateCount DontHaveCount", "String String String Int32 Int32 Int32 Int32", }); using (Spludlow.TempDirectory tempDir = new TempDirectory("")) { foreach (string softwareListName in softwareListNames) { DataTable romsTable = MameDatabase.SoftwareRomQuery("(sha1 IS NOT NULL) AND (softwarelist.name = '" + softwareListName + "')"); Dictionary> cloneofLookup = MameDatabase.SoftwareCloneofParentsLookup(softwareListName); DataTable softwareTable = database.Select(_SoftwareQueryText, softwareListName); foreach (DataRow softwareRow in softwareTable.Rows) { string softwareName = (string)softwareRow["name"]; DataRow reportRow = reportTable.Rows.Add(softwareListName, softwareName, "", 0, 0, 0, 0); Dictionary nameHashes = new Dictionary(); DataRow[] romRows = romsTable.Select("software_name = '" + softwareName + "'"); int parentCount = 0; int duplicateCount = 0; int dontHaveCount = 0; foreach (DataRow romRow in romRows) { string rom_name = (string)romRow["rom_name"]; string sha1 = ((string)romRow["sha1"]).ToUpper(); if (nameHashes.ContainsKey(rom_name) == true) { if (nameHashes[rom_name] != sha1) throw new ApplicationException("ROM name sha1 mismatch: " + softwareListName + ", " + softwareName + ", " + rom_name); ++duplicateCount; continue; } if (InParent(sha1, softwareName, cloneofLookup, romsTable) == true) { ++parentCount; continue; } if (hashStore.Exists(sha1) == false) { ++dontHaveCount; continue; } nameHashes.Add(rom_name, sha1); } reportRow["CopyCount"] = nameHashes.Count; reportRow["ParentCount"] = parentCount; reportRow["DuplicateCount"] = duplicateCount; reportRow["DontHaveCount"] = dontHaveCount; if (dontHaveCount > 0) { reportRow["Action"] = "Incomplete"; //continue; } if (nameHashes.Count == 0) { reportRow["Action"] = "Nothing"; continue; } string archiveFilename = targetDirectory + @"\" + softwareListName + @"\" + softwareName + ".zip"; if (File.Exists(archiveFilename) == true) { reportRow["Action"] = "Exists"; continue; } string archiveDirectory = tempDir.Path + @"\" + softwareName; Directory.CreateDirectory(archiveDirectory); foreach (string rom_name in nameHashes.Keys) { string storeFilename = hashStore.Filename(nameHashes[rom_name]); bool invalidName = MamePath.IsInvalidFilename(rom_name); string name = invalidName == false ? rom_name : MamePath.FixFilename(rom_name, '-'); if (invalidName == true) reportRow["Action"] = "Name: " + rom_name; string romFilename = archiveDirectory + @"\" + name; File.Copy(storeFilename, romFilename); } Spludlow.Archive.Create(archiveFilename, archiveDirectory + @"\*", false); Directory.Delete(archiveDirectory, true); } } } MameReport.Report("Exported Software Roms", reportTable); return "Exported Software Roms: " + reportTable.Rows.Count; } public static bool InParent(string sha1, string name, Dictionary> cloneofLookup, DataTable table) { if (cloneofLookup.ContainsKey(name) == false) return false; foreach (string parent in cloneofLookup[name]) { DataRow[] rows = table.Select("software_name = '" + parent + "' AND sha1 = '" + sha1 + "'"); if (rows.Length > 0) return true; } return false; } /// /// If has parent (software.cloneof IS NOT NULL) dont include if SHA1 in parent /// Disks in siblings that are duplicated (already exported on this export, within a software list) are omitted /// public static string ExportSoftwareDisk() { string[] softwareListNames = MameDatabase.SoftwareListsWithDisks(); return ExportSoftwareDisk(softwareListNames); } public static string ExportSoftwareDisk(string softwareListName) { return ExportSoftwareDisk(new string[] { softwareListName }); } public static string ExportSoftwareDisk(string[] softwareListNames) { MameConfiguration config = new MameConfiguration(); string targetDirectory = config.ExportDirectory; MameHashStores hashStores = new MameHashStores(); HashStore hashStore = hashStores.SoftwareDisk; Spludlow.Data.IDAL database = MameDatabase.Software(); DataTable reportTable = Spludlow.Data.TextTable.ReadText(new string[] { "SoftwareListName SoftwareListDescription SoftwareName SoftwareDescription DiskName CloneOf SHA1 Action", "String String String String String String String String", }); foreach (string softwareListName in softwareListNames) { DataTable disksTable = MameDatabase.SoftwareDiskQuery("(sha1 IS NOT NULL) AND (softwarelist.name = '" + softwareListName + "')"); Dictionary> cloneofLookup = MameDatabase.SoftwareCloneofParentsLookup(softwareListName); Dictionary nameHashes = new Dictionary(); foreach (DataRow diskRow in disksTable.Rows) { string softwarelist_name = (string)diskRow["softwarelist_name"]; string softwarelist_description = (string)diskRow["softwarelist_description"]; string software_name = (string)diskRow["software_name"]; string software_description = (string)diskRow["software_description"]; string disk_name = (string)diskRow["disk_name"]; string cloneof = ""; if (diskRow.IsNull("cloneof") == false) cloneof = (string)diskRow["cloneof"]; string sha1 = ((string)diskRow["sha1"]).ToUpper(); DataRow reportRow = reportTable.Rows.Add(softwarelist_name, softwarelist_description, software_name, software_description, disk_name, cloneof, sha1, ""); string keyName = software_name + @"\" + disk_name; if (nameHashes.ContainsKey(keyName) == true) throw new ApplicationException("Disk name duplicate: " + keyName); if (nameHashes.ContainsValue(sha1) == true) { reportRow["Action"] = "Sibling"; continue; } if (hashStore.Exists(sha1) == false) { reportRow["Action"] = "DontHave"; continue; } nameHashes.Add(keyName, sha1); bool invalidName = MamePath.IsInvalidFilename(disk_name); string name = invalidName == false ? disk_name : MamePath.FixFilename(disk_name, '-'); if (invalidName == true) reportRow["Action"] = "Name: " + disk_name; string targetFilename = targetDirectory + @"\" + softwarelist_name + @"\" + software_name + @"\" + name + ".chd"; if (File.Exists(targetFilename) == true) { reportRow["Action"] = "Exists"; continue; } string directoryName = Path.GetDirectoryName(targetFilename); if (Directory.Exists(directoryName) == false) Directory.CreateDirectory(directoryName); File.Copy(hashStore.Filename(sha1), targetFilename); } } MameReport.Report("Exported Software Disk", reportTable); return "Exported Software Disk: " + reportTable.Rows.Count; } } }