using System; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; using SabreTools.Filter; namespace SabreTools.DatItems { /// /// Represents the information specific to a set/game/machine /// [JsonObject("machine"), XmlRoot("machine")] public class Machine : ICloneable { #region Constants /// /// Trurip/EmuArc Machine developer /// public const string DeveloperKey = "DEVELOPER"; /// /// Trurip/EmuArc Game genre /// public const string GenreKey = "GENRE"; /// /// Trurip/EmuArc Title ID /// public const string TitleIDKey = "TITLEID"; #endregion #region Fields // 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 /// /// Internal Machine model /// [JsonIgnore] private Models.Metadata.Machine _machine = []; #endregion #region Accessors /// /// Get the value from a field based on the type provided /// /// Type of the value to get from the internal model /// Field to retrieve /// Value from the field, if possible public T? GetFieldValue(string? fieldName) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName)) return default; // Get the value based on the type return _machine.Read(fieldName!); } /// /// Set the value from a field based on the type provided /// /// Type of the value to set in the internal model /// Field to set /// Value to set /// True if the value was set, false otherwise public bool SetFieldValue(string? fieldName, T? value) { // Invalid field cannot be processed if (string.IsNullOrEmpty(fieldName)) return false; // Set the value based on the type _machine[fieldName!] = value; return true; } #endregion #region Constructors public Machine() { } public Machine(Models.Metadata.Machine machine) { // Get all fields to automatically copy without processing var nonItemFields = TypeHelper.GetConstants(typeof(Models.Metadata.Machine)); if (nonItemFields == null) return; // Populate the internal machine from non-filter fields _machine = []; foreach (string fieldName in nonItemFields) { if (machine.ContainsKey(fieldName)) _machine[fieldName] = machine[fieldName]; } } #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); /// /// Replace a field from another Machine /// /// Machine to replace field from /// Field to replace /// True if the replacement was successful, false otherwise public bool ReplaceField(Machine? other, string? fieldName) => FieldManipulator.ReplaceField(other?._machine, _machine, fieldName); /// /// Set a field in the Machine from a mapping string /// /// 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(string? fieldName, string value) => FieldManipulator.SetField(_machine, fieldName, value); #endregion } }