2020-12-09 21:52:38 -08:00
|
|
|
|
using System.IO;
|
2017-09-25 12:21:52 -07:00
|
|
|
|
using System.Linq;
|
2020-09-07 14:47:27 -07:00
|
|
|
|
using System.Xml.Serialization;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2020-12-08 13:23:59 -08:00
|
|
|
|
using SabreTools.Core;
|
2020-12-07 14:29:45 -08:00
|
|
|
|
using SabreTools.Logging;
|
2020-08-24 01:06:52 -07:00
|
|
|
|
using Newtonsoft.Json;
|
2016-10-26 22:10:47 -07:00
|
|
|
|
|
2020-12-08 16:37:08 -08:00
|
|
|
|
namespace SabreTools.DatFiles
|
2016-04-19 01:11:23 -07:00
|
|
|
|
{
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Represents a format-agnostic DAT
|
|
|
|
|
|
/// </summary>
|
2020-12-09 21:52:38 -08:00
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// The fact that this one class could be separated into as many partial
|
|
|
|
|
|
/// classes as it did means that the functionality here should probably
|
|
|
|
|
|
/// be split out into either separate classes or even an entirely separate
|
|
|
|
|
|
/// namespace. Also, with that in mind, each of the individual DatFile types
|
|
|
|
|
|
/// probably should only need to inherit from a thin abstract class and
|
|
|
|
|
|
/// should not be exposed as part of the library, instead being taken care
|
|
|
|
|
|
/// of behind the scenes as part of the reading and writing.
|
|
|
|
|
|
/// </remarks>
|
2020-09-08 10:12:41 -07:00
|
|
|
|
[JsonObject("datfile"), XmlRoot("datfile")]
|
2020-12-09 21:52:38 -08:00
|
|
|
|
public abstract partial class DatFile
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-31 14:04:10 -07:00
|
|
|
|
#region Fields
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-27 10:26:08 -07:00
|
|
|
|
/// Header values
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-10-07 15:42:30 -07:00
|
|
|
|
[JsonProperty("header"), XmlElement("header")]
|
2020-07-27 10:26:08 -07:00
|
|
|
|
public DatHeader Header { get; set; } = new DatHeader();
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-27 10:26:08 -07:00
|
|
|
|
/// DatItems and related statistics
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-10-07 15:42:30 -07:00
|
|
|
|
[JsonProperty("items"), XmlElement("items")]
|
2020-07-27 10:26:08 -07:00
|
|
|
|
public ItemDictionary Items { get; set; } = new ItemDictionary();
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2020-10-07 15:42:30 -07:00
|
|
|
|
#region Logging
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Logging object
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[JsonIgnore, XmlIgnore]
|
2020-10-07 16:37:10 -07:00
|
|
|
|
protected Logger logger;
|
2020-10-07 15:42:30 -07:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
#region Constructors
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// Create a new DatFile from an existing one
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <param name="datFile">DatFile to get the values from</param>
|
|
|
|
|
|
public DatFile(DatFile datFile)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-10-07 16:37:10 -07:00
|
|
|
|
logger = new Logger(this);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
if (datFile != null)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-27 10:26:08 -07:00
|
|
|
|
Header = datFile.Header;
|
2020-07-31 14:04:10 -07:00
|
|
|
|
Items = datFile.Items;
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// Create a specific type of DatFile to be used based on a format and a base DAT
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <param name="datFormat">Format of the DAT to be created</param>
|
|
|
|
|
|
/// <param name="baseDat">DatFile containing the information to use in specific operations</param>
|
2020-09-20 21:12:57 -07:00
|
|
|
|
/// <param name="quotes">For relevant types, assume the usage of quotes</param>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <returns>DatFile of the specific internal type that corresponds to the inputs</returns>
|
2020-09-20 21:12:57 -07:00
|
|
|
|
public static DatFile Create(DatFormat? datFormat = null, DatFile baseDat = null, bool quotes = true)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-15 09:41:59 -07:00
|
|
|
|
switch (datFormat)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.AttractMode:
|
|
|
|
|
|
return new AttractMode(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.ClrMamePro:
|
2020-09-20 21:12:57 -07:00
|
|
|
|
return new ClrMamePro(baseDat, quotes);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.CSV:
|
|
|
|
|
|
return new SeparatedValue(baseDat, ',');
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.DOSCenter:
|
|
|
|
|
|
return new DosCenter(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.EverdriveSMDB:
|
|
|
|
|
|
return new EverdriveSMDB(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.Listrom:
|
|
|
|
|
|
return new Listrom(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.Listxml:
|
|
|
|
|
|
return new Listxml(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.Logiqx:
|
|
|
|
|
|
return new Logiqx(baseDat, false);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.LogiqxDeprecated:
|
|
|
|
|
|
return new Logiqx(baseDat, true);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.MissFile:
|
|
|
|
|
|
return new Missfile(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.OfflineList:
|
|
|
|
|
|
return new OfflineList(baseDat);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.OpenMSX:
|
|
|
|
|
|
return new OpenMSX(baseDat);
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.RedumpMD5:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.MD5);
|
|
|
|
|
|
|
|
|
|
|
|
#if NET_FRAMEWORK
|
|
|
|
|
|
case DatFormat.RedumpRIPEMD160:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.RIPEMD160);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.RedumpSFV:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.CRC);
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.RedumpSHA1:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.SHA1);
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.RedumpSHA256:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.SHA256);
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.RedumpSHA384:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.SHA384);
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.RedumpSHA512:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.SHA512);
|
|
|
|
|
|
|
2020-09-04 15:02:15 -07:00
|
|
|
|
case DatFormat.RedumpSpamSum:
|
|
|
|
|
|
return new Hashfile(baseDat, Hash.SpamSum);
|
|
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.RomCenter:
|
|
|
|
|
|
return new RomCenter(baseDat);
|
|
|
|
|
|
|
2020-09-07 22:40:27 -07:00
|
|
|
|
case DatFormat.SabreJSON:
|
|
|
|
|
|
return new SabreJSON(baseDat);
|
|
|
|
|
|
|
2020-09-07 22:57:44 -07:00
|
|
|
|
case DatFormat.SabreXML:
|
|
|
|
|
|
return new SabreXML(baseDat);
|
|
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
case DatFormat.SoftwareList:
|
|
|
|
|
|
return new SoftwareList(baseDat);
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.SSV:
|
|
|
|
|
|
return new SeparatedValue(baseDat, ';');
|
|
|
|
|
|
|
|
|
|
|
|
case DatFormat.TSV:
|
|
|
|
|
|
return new SeparatedValue(baseDat, '\t');
|
|
|
|
|
|
|
|
|
|
|
|
// We use new-style Logiqx as a backup for generic DatFile
|
|
|
|
|
|
case null:
|
|
|
|
|
|
default:
|
|
|
|
|
|
return new Logiqx(baseDat, false);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// Create a new DatFile from an existing DatHeader
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <param name="datHeader">DatHeader to get the values from</param>
|
|
|
|
|
|
public static DatFile Create(DatHeader datHeader)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-15 09:41:59 -07:00
|
|
|
|
DatFile datFile = Create(datHeader.DatFormat);
|
2020-07-27 10:26:08 -07:00
|
|
|
|
datFile.Header = (DatHeader)datHeader.Clone();
|
2020-07-15 09:41:59 -07:00
|
|
|
|
return datFile;
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// Add items from another DatFile to the existing DatFile
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <param name="datFile">DatFile to add from</param>
|
|
|
|
|
|
/// <param name="delete">If items should be deleted from the source DatFile</param>
|
|
|
|
|
|
public void AddFromExisting(DatFile datFile, bool delete = false)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-15 09:41:59 -07:00
|
|
|
|
// Get the list of keys from the DAT
|
2020-08-21 23:24:32 -07:00
|
|
|
|
var keys = datFile.Items.Keys.ToList();
|
|
|
|
|
|
foreach (string key in keys)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-15 09:41:59 -07:00
|
|
|
|
// Add everything from the key to the internal DAT
|
2020-07-26 22:34:45 -07:00
|
|
|
|
Items.AddRange(key, datFile.Items[key]);
|
2020-07-15 09:41:59 -07:00
|
|
|
|
|
|
|
|
|
|
// Now remove the key from the source DAT
|
|
|
|
|
|
if (delete)
|
2020-07-26 22:34:45 -07:00
|
|
|
|
datFile.Items.Remove(key);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
2020-07-15 09:41:59 -07:00
|
|
|
|
|
|
|
|
|
|
// Now remove the file dictionary from the source DAT
|
|
|
|
|
|
if (delete)
|
2020-07-26 22:34:45 -07:00
|
|
|
|
datFile.Items = null;
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// Apply a DatHeader to an existing DatFile
|
2019-01-08 11:49:31 -08:00
|
|
|
|
/// </summary>
|
2020-07-15 09:41:59 -07:00
|
|
|
|
/// <param name="datHeader">DatHeader to get the values from</param>
|
|
|
|
|
|
public void ApplyDatHeader(DatHeader datHeader)
|
2019-01-08 11:49:31 -08:00
|
|
|
|
{
|
2020-07-27 10:26:08 -07:00
|
|
|
|
Header.ConditionalCopy(datHeader);
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-27 20:56:50 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Fill the header values based on existing Header and path
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="path">Path used for creating a name, if necessary</param>
|
|
|
|
|
|
/// <param name="bare">True if the date should be omitted from name and description, false otherwise</param>
|
|
|
|
|
|
public void FillHeaderFromPath(string path, bool bare)
|
|
|
|
|
|
{
|
|
|
|
|
|
// If the description is defined but not the name, set the name from the description
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(Header.Name) && !string.IsNullOrWhiteSpace(Header.Description))
|
|
|
|
|
|
{
|
|
|
|
|
|
Header.Name = Header.Description;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the name is defined but not the description, set the description from the name
|
|
|
|
|
|
else if (!string.IsNullOrWhiteSpace(Header.Name) && string.IsNullOrWhiteSpace(Header.Description))
|
|
|
|
|
|
{
|
|
|
|
|
|
Header.Description = Header.Name + (bare ? string.Empty : $" ({Header.Date})");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If neither the name or description are defined, set them from the automatic values
|
|
|
|
|
|
else if (string.IsNullOrWhiteSpace(Header.Name) && string.IsNullOrWhiteSpace(Header.Description))
|
|
|
|
|
|
{
|
|
|
|
|
|
string[] splitpath = path.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
|
|
|
|
|
|
Header.Name = splitpath.Last();
|
|
|
|
|
|
Header.Description = Header.Name + (bare ? string.Empty : $" ({Header.Date})");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-15 09:41:59 -07:00
|
|
|
|
#endregion
|
2019-01-08 11:49:31 -08:00
|
|
|
|
}
|
2016-04-19 01:11:23 -07:00
|
|
|
|
}
|