// 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;
}
}
}