using System; using System.Xml.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; using SabreTools.Core.Tools; using SabreTools.Filter; namespace SabreTools.DatItems { /// /// Represents the information specific to a set/game/machine /// [JsonObject("machine"), XmlRoot("machine")] public class Machine : ICloneable { #region Fields #region Common /// /// Name of the machine /// [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Include)] [XmlElement("name")] public string? Name { get => _machine.ReadString(Models.Metadata.Machine.NameKey); set => _machine[Models.Metadata.Machine.NameKey] = value; } /// /// Additional notes /// /// Known as "Extra" in AttractMode [JsonProperty("comment", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("comment")] public string? Comment { get => _machine.ReadString(Models.Metadata.Machine.CommentKey); set => _machine[Models.Metadata.Machine.CommentKey] = value; } /// /// Extended description /// [JsonProperty("description", DefaultValueHandling = DefaultValueHandling.Include)] [XmlElement("description")] public string? Description { get => _machine.ReadString(Models.Metadata.Machine.DescriptionKey); set => _machine[Models.Metadata.Machine.DescriptionKey] = value; } /// /// Year(s) of release/manufacture /// [JsonProperty("year", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("year")] public string? Year { get => _machine.ReadString(Models.Metadata.Machine.YearKey); set => _machine[Models.Metadata.Machine.YearKey] = value; } /// /// Manufacturer, if available /// [JsonProperty("manufacturer", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("manufacturer")] public string? Manufacturer { get => _machine.ReadString(Models.Metadata.Machine.ManufacturerKey); set => _machine[Models.Metadata.Machine.ManufacturerKey] = value; } /// /// Publisher, if available /// [JsonProperty("publisher", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("publisher")] public string? Publisher { get => _machine.ReadString(Models.Metadata.Machine.PublisherKey); set => _machine[Models.Metadata.Machine.PublisherKey] = value; } /// /// Category, if available /// [JsonProperty("category", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("category")] public string? Category { get => _machine.ReadString(Models.Metadata.Machine.CategoryKey); set => _machine[Models.Metadata.Machine.CategoryKey] = value; } /// /// fomof parent /// [JsonProperty("romof", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("romof")] public string? RomOf { get => _machine.ReadString(Models.Metadata.Machine.RomOfKey); set => _machine[Models.Metadata.Machine.RomOfKey] = value; } /// /// cloneof parent /// [JsonProperty("cloneof", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("cloneof")] public string? CloneOf { get => _machine.ReadString(Models.Metadata.Machine.CloneOfKey); set => _machine[Models.Metadata.Machine.CloneOfKey] = value; } /// /// sampleof parent /// [JsonProperty("sampleof", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("sampleof")] public string? SampleOf { get => _machine.ReadString(Models.Metadata.Machine.SampleOfKey); set => _machine[Models.Metadata.Machine.SampleOfKey] = value; } /// /// Type of the machine /// [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonConverter(typeof(StringEnumConverter))] [XmlElement("type")] public MachineType MachineType { get { bool? isBios = _machine.ReadBool(Models.Metadata.Machine.IsBiosKey); bool? isDevice = _machine.ReadBool(Models.Metadata.Machine.IsDeviceKey); bool? isMechanical = _machine.ReadBool(Models.Metadata.Machine.IsMechanicalKey); MachineType machineType = MachineType.None; if (isBios == true) machineType |= MachineType.Bios; if (isDevice == true) machineType |= MachineType.Device; if (isMechanical == true) machineType |= MachineType.Mechanical; return machineType; } set { #if NETFRAMEWORK if ((value & MachineType.Bios) != 0) _machine[Models.Metadata.Machine.IsBiosKey] = "yes"; if ((value & MachineType.Device) != 0) _machine[Models.Metadata.Machine.IsDeviceKey] = "yes"; if ((value & MachineType.Mechanical) != 0) _machine[Models.Metadata.Machine.IsMechanicalKey] = "yes"; #else if (value.HasFlag(MachineType.Bios)) _machine[Models.Metadata.Machine.IsBiosKey] = "yes"; if (value.HasFlag(MachineType.Device)) _machine[Models.Metadata.Machine.IsDeviceKey] = "yes"; if (value.HasFlag(MachineType.Mechanical)) _machine[Models.Metadata.Machine.IsMechanicalKey] = "yes"; #endif } } [JsonIgnore] public bool MachineTypeSpecified { get { return MachineType != 0x0 && MachineType != MachineType.None; } } #endregion #region AttractMode /// /// Player count /// /// Also in Logiqx EmuArc [JsonProperty("players", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("players")] public string? Players { get => _machine.ReadString(Models.Metadata.Machine.PlayersKey); set => _machine[Models.Metadata.Machine.PlayersKey] = value; } /// /// Screen rotation /// [JsonProperty("rotation", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("rotation")] public string? Rotation { get => _machine.ReadString(Models.Metadata.Machine.RotationKey); set => _machine[Models.Metadata.Machine.RotationKey] = value; } /// /// Control method /// [JsonProperty("control", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("control")] public string? Control { get => _machine.ReadString(Models.Metadata.Machine.ControlKey); set => _machine[Models.Metadata.Machine.ControlKey] = value; } /// /// Support status /// [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("status")] public string? Status { get => _machine.ReadString(Models.Metadata.Machine.StatusKey); set => _machine[Models.Metadata.Machine.StatusKey] = value; } /// /// Display count /// [JsonProperty("displaycount", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("displaycount")] public string? DisplayCount { get => _machine.ReadString(Models.Metadata.Machine.DisplayCountKey); set => _machine[Models.Metadata.Machine.DisplayCountKey] = value; } /// /// Display type /// [JsonProperty("displaytype", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("displaytype")] public string? DisplayType { get => _machine.ReadString(Models.Metadata.Machine.DisplayTypeKey); set => _machine[Models.Metadata.Machine.DisplayTypeKey] = value; } /// /// Number of input buttons /// [JsonProperty("buttons", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("buttons")] public string? Buttons { get => _machine.ReadString(Models.Metadata.Machine.ButtonsKey); set => _machine[Models.Metadata.Machine.ButtonsKey] = value; } #endregion #region ListXML /// /// History.dat entry for the machine /// [JsonProperty("history", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("history")] public string? History { get => _machine.ReadString(Models.Metadata.Machine.HistoryKey); set => _machine[Models.Metadata.Machine.HistoryKey] = value; } /// /// Emulator source file related to the machine /// /// Also in Logiqx [JsonProperty("sourcefile", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("sourcefile")] public string? SourceFile { get => _machine.ReadString(Models.Metadata.Machine.SourceFileKey); set => _machine[Models.Metadata.Machine.SourceFileKey] = value; } /// /// Machine runnable status /// /// Also in Logiqx [JsonProperty("runnable", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("runnable")] public Runnable Runnable { get => _machine.ReadString(Models.Metadata.Machine.RunnableKey).AsEnumValue(); set => _machine[Models.Metadata.Machine.RunnableKey] = value.AsStringValue(); } [JsonIgnore] public bool RunnableSpecified { get { return Runnable != Runnable.NULL; } } #endregion #region Logiqx /// /// Machine board name /// [JsonProperty("board", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("board")] public string? Board { get => _machine.ReadString(Models.Metadata.Machine.BoardKey); set => _machine[Models.Metadata.Machine.BoardKey] = value; } /// /// Rebuild location if different than machine name /// [JsonProperty("rebuildto", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("rebuildto")] public string? RebuildTo { get => _machine.ReadString(Models.Metadata.Machine.RebuildToKey); set => _machine[Models.Metadata.Machine.RebuildToKey] = value; } /// /// No-Intro ID for the game /// [JsonProperty("nointroid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("nointroid")] public string? NoIntroId { get => _machine.ReadString(Models.Metadata.Machine.IdKey); set => _machine[Models.Metadata.Machine.IdKey] = value; } /// /// No-Intro ID for the game /// [JsonProperty("nointrocloneofid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("nointrocloneofid")] public string? NoIntroCloneOfId { get => _machine.ReadString(Models.Metadata.Machine.CloneOfIdKey); set => _machine[Models.Metadata.Machine.CloneOfIdKey] = value; } #endregion // TODO: Should this be a separate object for TruRip? #region Logiqx EmuArc /// /// Title ID /// [JsonProperty("titleid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("titleid")] public string? TitleID { get; set; } = null; /// /// Machine developer /// [JsonProperty("developer", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("developer")] public string? Developer { get; set; } = null; /// /// Game genre /// [JsonProperty("genre", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("genre")] public string? Genre { get; set; } = null; /// /// Game subgenre /// [JsonProperty("subgenre", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("subgenre")] public string? Subgenre { get; set; } = null; /// /// Game ratings /// [JsonProperty("ratings", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("ratings")] public string? Ratings { get; set; } = null; /// /// Game score /// [JsonProperty("score", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("score")] public string? Score { get; set; } = null; /// /// Is the machine enabled /// [JsonProperty("enabled", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("enabled")] public string? Enabled { get; set; } = null; // bool? /// /// Does the game have a CRC check /// [JsonProperty("hascrc", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("hascrc")] public bool? Crc { get; set; } = null; [JsonIgnore] public bool CrcSpecified { get { return Crc != null; } } /// /// Machine relations /// [JsonProperty("relatedto", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("relatedto")] public string? RelatedTo { get; set; } = null; #endregion #region OpenMSX /// /// Generation MSX ID /// [JsonProperty("genmsxid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("genmsxid")] public string? GenMSXID { get => _machine.ReadString(Models.Metadata.Machine.GenMSXIDKey); set => _machine[Models.Metadata.Machine.GenMSXIDKey] = value; } /// /// MSX System /// [JsonProperty("system", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("system")] public string? System { get => _machine.ReadString(Models.Metadata.Machine.SystemKey); set => _machine[Models.Metadata.Machine.SystemKey] = value; } /// /// Machine country of origin /// [JsonProperty("country", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("country")] public string? Country { get => _machine.ReadString(Models.Metadata.Machine.CountryKey); set => _machine[Models.Metadata.Machine.CountryKey] = value; } #endregion #region SoftwareList /// /// Support status /// [JsonProperty("supported", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("supported")] public Supported Supported { get => _machine.ReadString(Models.Metadata.Machine.SupportedKey).AsEnumValue(); set => _machine[Models.Metadata.Machine.SupportedKey] = value.AsStringValue(useSecond: true); } [JsonIgnore] public bool SupportedSpecified { get { return Supported != Supported.NULL; } } #endregion /// /// Internal Machine model /// [JsonIgnore] private Models.Metadata.Machine _machine = []; #endregion // Fields #region Constructors /// /// Create a new Machine object /// public Machine() { } /// /// Create a new Machine object with the included information /// /// Name of the machine /// Description of the machine public Machine(string name, string description) { Name = name; Description = description; } #endregion #region Cloning methods /// /// Create a clone of the current machine /// /// New machine with the same values as the current one public object Clone() { return new Machine() { #region Common _machine = this._machine.Clone() as Models.Metadata.Machine ?? [], #endregion #region Logiqx EmuArc TitleID = this.TitleID, Developer = this.Developer, Genre = this.Genre, Subgenre = this.Subgenre, Ratings = this.Ratings, Score = this.Score, Enabled = this.Enabled, Crc = this.Crc, RelatedTo = this.RelatedTo, #endregion }; } #endregion #region Manipulation /// /// Runs a filter and determines if it passes or not /// /// Filter runner to use for checking /// True if the Machine passes the filter, false otherwise public bool PassesFilter(FilterRunner filterRunner) => filterRunner.Run(_machine); /// /// Remove a field from the Machine /// /// Field to remove /// True if the removal was successful, false otherwise public bool RemoveField(string? fieldName) => FieldManipulator.RemoveField(_machine, fieldName); /// /// Set a field in the Machine from a mapping string /// /// Machine field to set /// String representing the value to set /// True if the setting was successful, false otherwise /// This only performs minimal validation before setting public bool SetField(MachineField machineField, string value) { // Get the correct internal field name string? fieldName = machineField switch { MachineField.Board => Models.Metadata.Machine.BoardKey, MachineField.Buttons => Models.Metadata.Machine.ButtonsKey, MachineField.Category => Models.Metadata.Machine.CategoryKey, MachineField.CloneOf => Models.Metadata.Machine.CloneOfKey, MachineField.CloneOfID => Models.Metadata.Machine.CloneOfIdKey, MachineField.Comment => Models.Metadata.Machine.CommentKey, MachineField.Control => Models.Metadata.Machine.ControlKey, MachineField.Country => Models.Metadata.Machine.CountryKey, //MachineField.CRC => Models.Metadata.Machine.CRCKey, MachineField.Description => Models.Metadata.Machine.DescriptionKey, //MachineField.Developer => Models.Metadata.Machine.DeveloperKey, MachineField.DisplayCount => Models.Metadata.Machine.DisplayCountKey, MachineField.DisplayType => Models.Metadata.Machine.DisplayTypeKey, //MachineField.Enabled => Models.Metadata.Machine.EnabledKey, MachineField.GenMSXID => Models.Metadata.Machine.GenMSXIDKey, //MachineField.Genre => Models.Metadata.Machine.GenreKey, MachineField.History => Models.Metadata.Machine.HistoryKey, MachineField.ID => Models.Metadata.Machine.IdKey, MachineField.Manufacturer => Models.Metadata.Machine.ManufacturerKey, MachineField.Name => Models.Metadata.Machine.NameKey, MachineField.Players => Models.Metadata.Machine.PlayersKey, MachineField.Publisher => Models.Metadata.Machine.PublisherKey, //MachineField.Ratings => Models.Metadata.Machine.RatingsKey, MachineField.RebuildTo => Models.Metadata.Machine.RebuildToKey, //MachineField.RelatedTo => Models.Metadata.Machine.RelatedToKey, MachineField.RomOf => Models.Metadata.Machine.RomOfKey, MachineField.Rotation => Models.Metadata.Machine.RotationKey, MachineField.Runnable => Models.Metadata.Machine.RunnableKey, MachineField.SampleOf => Models.Metadata.Machine.SampleOfKey, //MachineField.Score => Models.Metadata.Machine.ScoreKey, MachineField.SourceFile => Models.Metadata.Machine.SourceFileKey, MachineField.Status => Models.Metadata.Machine.StatusKey, //MachineField.Subgenre => Models.Metadata.Machine.SubgenreKey, MachineField.Supported => Models.Metadata.Machine.SupportedKey, MachineField.System => Models.Metadata.Machine.SystemKey, //MachineField.TitleID => Models.Metadata.Machine.TitleIDKey, //MachineField.Type => Models.Metadata.Machine.TypeKey, MachineField.Year => Models.Metadata.Machine.YearKey, _ => null, }; // A null value means special handling is needed if (fieldName == null) { switch (machineField) { case MachineField.CRC: Crc = value.AsYesNo(); return true; case MachineField.Developer: Developer = value; return true; case MachineField.Enabled: Enabled = value; return true; case MachineField.Genre: Genre = value; return true; case MachineField.Ratings: Ratings = value; return true; case MachineField.RelatedTo: RelatedTo = value; return true; case MachineField.Score: Score = value; return true; case MachineField.Subgenre: Subgenre = value; return true; case MachineField.TitleID: TitleID = value; return true; case MachineField.Type: MachineType = value.AsEnumValue(); return true; } } // Remove the field and return return FieldManipulator.SetField(_machine, fieldName, value); } #endregion } }