/// <summary> /// Creates a new instance if the UnmergedProgram structure. /// </summary> /// <param name="code">INTV Funhouse code for the ROM.</param> /// <param name="title">The title of the program (typically title screen title).</param> /// <param name="vendor">Vendor who wrote the program.</param> /// <param name="year">The year the program was copyrighted (or released).</param> /// <param name="crcData">The CRC values of known versions of the program.</param> /// <param name="crcDescriptions">The descriptions of the known versions of the program for each CRC.</param> /// <param name="generalFeatures">Generic program features.</param> /// <param name="cuttleCart3">Cuttle Cart 3 features.</param> internal UnmergedProgram(string code, string title, string vendor, string year, uint[] crcData, string[] crcDescriptions, GeneralFeatures generalFeatures, CuttleCart3Features cuttleCart3) { Code = code; CrcData = crcData; CrcDescriptions = crcDescriptions; CrcCfgs = Enumerable.Repeat(-1, crcData.Count()).ToArray(); Year = year; Title = title; Vendor = vendor; Features = new ProgramFeatures(); Features.GeneralFeatures = generalFeatures; Features.CuttleCart3 = cuttleCart3; }
/// <summary> /// Creates a new instance if the UnmergedProgram structure. /// </summary> /// <param name="code">INTV Funhouse code for the ROM.</param> /// <param name="title">The title of the program (typically title screen title).</param> /// <param name="vendor">Vendor who wrote the program.</param> /// <param name="year">The year the program was copyrighted (or released).</param> /// <param name="crcData">The CRC values of known versions of the program.</param> /// <param name="crcDescriptions">The descriptions of the known versions of the program for each CRC.</param> /// <param name="generalFeatures">Generic program features.</param> /// <param name="intellivoice">Intellivoice compatibility.</param> internal UnmergedProgram(string code, string title, string vendor, string year, uint[] crcData, string[] crcDescriptions, GeneralFeatures generalFeatures, FeatureCompatibility intellivoice) { Code = code; CrcData = crcData; CrcDescriptions = crcDescriptions; CrcCfgs = Enumerable.Repeat((generalFeatures == GeneralFeatures.SystemRom) ? -1 : 0, crcData.Count()).ToArray(); Year = year; Title = title; Vendor = vendor; Features = new ProgramFeatures(); Features.GeneralFeatures = generalFeatures; Features.Intellivoice = intellivoice; }
/// <summary> /// Creates a new instance if the UnmergedProgram structure. /// </summary> /// <param name="code">INTV Funhouse code for the ROM.</param> /// <param name="title">The title of the program (typically title screen title).</param> /// <param name="vendor">Vendor who wrote the program.</param> /// <param name="year">The year the program was copyrighted (or released).</param> /// <param name="crcData">The CRC values of known versions of the program.</param> /// <param name="crcDescriptions">The descriptions of the known versions of the program for each CRC.</param> /// <param name="generalFeatures">Generic program features.</param> /// <param name="keyboardComponent">Keyboard component features.</param> internal UnmergedProgram(string code, string title, string vendor, string year, uint[] crcData, string[] crcDescriptions, GeneralFeatures generalFeatures, KeyboardComponentFeatures keyboardComponent) { Code = code; CrcData = crcData; CrcDescriptions = crcDescriptions; CrcCfgs = Enumerable.Repeat(-1, crcData.Count()).ToArray(); Year = year; Title = title; Vendor = vendor; Features = new ProgramFeatures(); Features.GeneralFeatures = generalFeatures; Features.KeyboardComponent = keyboardComponent; }
/// <summary> /// Initializes a new instance of the <see cref="INTV.Core.Model.Program.LuigiFileMetadataProgramInformation"/> class. /// </summary> /// <param name="header">A LUIGI file header that describes the ROM's features.</param> /// <param name="metadata">Additional ROM metadata, if any.</param> public LuigiFileMetadataProgramInformation(LuigiFileHeader header, LuigiMetadataBlock metadata) { _features = ProgramFeatures.Combine(header.Features.ToProgramFeatures(), header.Features2.ToProgramFeatures()); _crc = new CrcData(header.OriginalRomCrc32, string.Empty, _features.ToIncompatibilityFlags()); Metadata = metadata; if (metadata != null) { _title = metadata.LongNames.FirstOrDefault(); _vendor = metadata.Publishers.FirstOrDefault(); if (metadata.ReleaseDates.Any()) { _year = metadata.ReleaseDates.First().Date.Year.ToString(); } _shortName = metadata.ShortNames.FirstOrDefault(); } }
/// <summary> /// Creates a new instance of the UnmergedProgramInformation class. /// </summary> /// <param name="code">INTV Funhouse database code of the program.</param> /// <param name="title">Title of the program.</param> /// <param name="vendor">Program vendor.</param> /// <param name="year">The year the program was copyrighted (or released).</param> /// <param name="crcData">Known CRC values for the program ROM variants.</param> /// <param name="crcDescriptions">Descriptions of the ROM variations.</param> /// <param name="crcCfgs">The default .cfg file to use if one is not provided.</param> /// <param name="features">The program's features.</param> internal UnmergedProgramInformation(string code, string title, string vendor, string year, uint[] crcData, string[] crcDescriptions, int[] crcCfgs, ProgramFeatures features) { Code = code; _title = title; _vendor = vendor; _year = year; _crc = new List <CrcData>(); for (int i = 0; i < crcData.Length; ++i) { var crcDescription = string.Empty; if (i < crcDescriptions.Length) { crcDescription = crcDescriptions[i]; } _crc.Add(new CrcData(crcData[i], crcDescription, IncompatibilityFlags.None, crcCfgs[i])); } _features = features; }
/// <summary> /// Initializes a new instance of the <see cref="INTV.Core.Model.Program.RomFileMetadataProgramInformation"/> class. /// </summary> /// <param name="rom">The ROM whose metadata program information is desired. If not a RomFormatRom, the /// metadata will be empty, and the features will be generic unrecognized ROM features.</param> public RomFileMetadataProgramInformation(IRom rom) { _features = ProgramFeatures.GetUnrecognizedRomFeatures(); Metadata = Enumerable.Empty <RomMetadataBlock>(); var romFormatRom = Rom.AsSpecificRomType <RomFormatRom>(rom); if (romFormatRom != null) { Metadata = romFormatRom.Metadata; var stringMetaData = Metadata.FirstOrDefault(m => m.Type == RomMetadataIdTag.Title) as RomMetadataString; if ((stringMetaData != null) && !string.IsNullOrEmpty(stringMetaData.StringValue)) { _title = stringMetaData.StringValue; } stringMetaData = Metadata.FirstOrDefault(m => m.Type == RomMetadataIdTag.ShortTitle) as RomMetadataString; if ((stringMetaData != null) && !string.IsNullOrEmpty(stringMetaData.StringValue)) { _shortName = stringMetaData.StringValue; } var date = Metadata.OfType <RomMetadataDate>().FirstOrDefault(d => d.Type == RomMetadataIdTag.ReleaseDate); if ((date != null) && date.Date.Flags.HasFlag(MetadataDateTimeFlags.Year)) { _year = date.Date.Date.Year.ToString(); } var vendor = Metadata.OfType <RomMetadataPublisher>().FirstOrDefault(); if (vendor != null) { _vendor = vendor.Publisher; } var features = Metadata.OfType <RomMetadataFeatures>().FirstOrDefault(); if (features != null) { _features = features.Features; } _crc = new CrcData(romFormatRom.Crc, string.Empty, _features.ToIncompatibilityFlags()); } }
public CfgFileMetadataProgramInformation(IRom rom) { _features = ProgramFeatures.GetUnrecognizedRomFeatures(); Metadata = Enumerable.Empty <CfgVarMetadataBlock>(); var binFormatRom = Rom.AsSpecificRomType <BinFormatRom>(rom); if (binFormatRom != null) { Metadata = binFormatRom.Metadata; var stringMetaData = Metadata.FirstOrDefault(m => m.Type == CfgVarMetadataIdTag.Name) as CfgVarMetadataString; if ((stringMetaData != null) && !string.IsNullOrEmpty(stringMetaData.StringValue)) { _title = stringMetaData.StringValue; } stringMetaData = Metadata.FirstOrDefault(m => m.Type == CfgVarMetadataIdTag.ShortName) as CfgVarMetadataString; if ((stringMetaData != null) && !string.IsNullOrEmpty(stringMetaData.StringValue)) { _shortName = stringMetaData.StringValue; } var date = Metadata.OfType <CfgVarMetadataDate>().FirstOrDefault(d => d.Type == CfgVarMetadataIdTag.ReleaseDate); if ((date != null) && date.Date.Flags.HasFlag(MetadataDateTimeFlags.Year)) { _year = date.Date.Date.Year.ToString(); } var vendor = Metadata.FirstOrDefault(m => m.Type == CfgVarMetadataIdTag.Publisher) as CfgVarMetadataString; if (vendor != null) { _vendor = vendor.StringValue; } // NOTE: If these are specified multiple times, the 'last one wins' rule will be in effect. // That's technically OK, since the behavior is unspecified. From as1600.txt: // // Lines marked with a "*" can be repeated. For example, if a game has multiple // authors, you can list them all with their own author variable. Likewise, if // a program was released multiple times, you can give a list of release dates. // // For other values, repeating a variable does not have a well defined meaning. // Typically (but not always), the first instance takes precedence. // // So in the case of specifying features multiple times, this code will result in // 'last one wins'. foreach (var feature in Metadata.OfType <CfgVarMetadataFeatureCompatibility>()) { switch (feature.Type) { case CfgVarMetadataIdTag.Ecs: case CfgVarMetadataIdTag.EcsCompatibility: _features.Ecs = (EcsFeatures)feature.Compatibility; break; case CfgVarMetadataIdTag.Voice: case CfgVarMetadataIdTag.IntellivoiceCompatibility: _features.Intellivoice = feature.Compatibility; break; case CfgVarMetadataIdTag.IntellivisionII: case CfgVarMetadataIdTag.IntellivisionIICompatibility: _features.IntellivisionII = feature.Compatibility; break; case CfgVarMetadataIdTag.KeyboardComponentCompatibility: _features.KeyboardComponent = (KeyboardComponentFeatures)feature.Compatibility; break; case CfgVarMetadataIdTag.TutorvisionCompatibility: _features.Tutorvision = feature.Compatibility; break; case CfgVarMetadataIdTag.JlpAccelerators: case CfgVarMetadataIdTag.Jlp: _features.Jlp = (JlpFeatures)feature.Compatibility; if (_features.Jlp != JlpFeatures.Incompatible) { _features.JlpHardwareVersion = JlpHardwareVersion.Jlp03; // Assume minimal hardware version needed if (_features.Jlp > JlpFeatures.Tolerates) { // Flash storage is indicated, so check for it. var flashStorage = Metadata.FirstOrDefault(m => m.Type == CfgVarMetadataIdTag.JlpFlash) as CfgVarMetadataInteger; if ((flashStorage != null) && (flashStorage.IntegerValue > 0)) { // TODO: Min value checking here? _features.JlpFlashMinimumSaveSectors = (ushort)flashStorage.IntegerValue; _features.Jlp |= JlpFeatures.SaveDataRequired; } } } break; default: break; } } var ltoMapper = Metadata.FirstOrDefault(m => m.Type == CfgVarMetadataIdTag.LtoFlashMapper) as CfgVarMetadataBoolean; if (ltoMapper != null) { _features.LtoFlash = LtoFlashFeatures.Requires | LtoFlashFeatures.LtoFlashMemoryMapped; } _crc = new CrcData(binFormatRom.Crc, string.Empty, _features.ToIncompatibilityFlags()); } }
/// <summary> /// Merges the given IProgramInformation data to form a new, combined version of the information. /// </summary> /// <param name="programInformation">The "primary" source of information.</param> /// <param name="fieldsToMerge">Identifies which fields to merge.</param> /// <param name="otherSources">The other information sources to merge, and how to merge them. See Remarks.</param> /// <returns>The merged program information.</returns> /// <remarks>Each of the additional information sources in the merge will be combined with the primary. The newly /// merged information is initialized using the data from <paramref name="programInformation"/>. The /// other sources should be ordered such that the first entry is the "most important" and the final entry the /// "least important". Each entry also describes which fields it is allowed to override in the merge. /// This is to offer a means of conflict resolution in situations arising from multiple sources containing /// the same information and attempting to override the default. For example, consider the case in which /// <paramref name="programInformation"/> and two entries are provided via <paramref name="otherSources"/>, /// all three of which define a value for <see cref="IProgramInformation.Title"/>. Here are some ways in which /// this can be configured: /// <code> /// fieldsToMerge = ProgramInformationMergeFieldsFlags.None; /// </code> /// In this case, the information in <paramref name="programInformation"/> will not be changed. A copy of the /// data within it will be returned. Note, however, that the specific implementation of <see cref="IProgramInformation"/> /// used to deliver the resulting data may be different than the original! /// Now, consider this scenario: /// <code> /// fieldsToMerge = ProgramInformationMergeFieldsFlags.Title; /// otherSources[0].Item2 = ProgramInformationMergeFieldsFlags.Title; /// otherSources[1].Item2 = ProgramInformationMergeFieldsFlags.Title; /// </code> /// In this case, each entry allows 'Title' to be set. Because <paramref name="programInformation"/> is /// treated as the highest priority, its value will be retained unless it is not set (in this case, a <c>null</c> /// or empty string). So, if programInformation.Title has no value, but otherSources[0].Item1.Title does, then /// the final result contains otherSources[0].Item1.Title, regardless of the value of otherSources[1].Item1.Title. /// By carefully considering these flags, you can have a reasonably rich order-of-precedence defined for establishing /// the data in the merged result. /// </remarks> /// <exception cref="System.ArgumentException">Thrown if unrecognized flags are provided in <paramref name="fieldsToMerge"/> or via <paramref name="otherSources"/>.</exception> public static IProgramInformation Merge(this IProgramInformation programInformation, ProgramInformationMergeFieldsFlags fieldsToMerge, params Tuple <IProgramInformation, ProgramInformationMergeFieldsFlags>[] otherSources) { // Grr.... PCLs (at least in .NET 4.0) don't support Enum.GetValues() var flagsToProcess = new[] { ProgramInformationMergeFieldsFlags.Title, ProgramInformationMergeFieldsFlags.Vendor, ProgramInformationMergeFieldsFlags.Year, ProgramInformationMergeFieldsFlags.Features, ProgramInformationMergeFieldsFlags.ShortName, ProgramInformationMergeFieldsFlags.Crcs }; var unknownFlagsForMerge = otherSources.Select(s => s.Item2).Concat(new[] { fieldsToMerge }).Except(flagsToProcess).Except(new[] { ProgramInformationMergeFieldsFlags.None, ProgramInformationMergeFieldsFlags.All }); if (unknownFlagsForMerge.Any()) { var unknownFlags = unknownFlagsForMerge.Aggregate((all, flag) => all | flag); throw new ArgumentException(string.Format(Resources.Strings.ProgramInformation_InvalidFieldFormat, unknownFlags)); } var mergedProgramInformation = new UserSpecifiedProgramInformation(programInformation); var mergedPriorityFlags = fieldsToMerge; // accumulates if a "higher priority" info has already claimed a field foreach (var otherSource in otherSources) { var otherInformation = otherSource.Item1; var updateFlags = otherSource.Item2; foreach (var flag in flagsToProcess) { if (updateFlags.HasFlag(flag)) { switch (flag) { case ProgramInformationMergeFieldsFlags.Title: if (string.IsNullOrEmpty(mergedProgramInformation.Title) || !mergedPriorityFlags.HasFlag(flag)) { mergedProgramInformation.Title = otherInformation.Title; if (!string.IsNullOrEmpty(mergedProgramInformation.Title)) { mergedPriorityFlags |= flag; } } break; case ProgramInformationMergeFieldsFlags.Vendor: if (string.IsNullOrEmpty(mergedProgramInformation.Vendor) || !mergedPriorityFlags.HasFlag(flag)) { mergedProgramInformation.Vendor = otherInformation.Vendor; if (!string.IsNullOrEmpty(mergedProgramInformation.Vendor)) { mergedPriorityFlags |= flag; } } break; case ProgramInformationMergeFieldsFlags.Year: if (string.IsNullOrEmpty(mergedProgramInformation.Year) || !mergedPriorityFlags.HasFlag(flag)) { mergedProgramInformation.Year = otherInformation.Year; if (!string.IsNullOrEmpty(mergedProgramInformation.Year)) { mergedPriorityFlags |= flag; } } break; case ProgramInformationMergeFieldsFlags.Features: // This runs the risk of combining conflicting flags... mergedProgramInformation.Features = ProgramFeatures.Combine(mergedProgramInformation.Features, otherInformation.Features); mergedPriorityFlags |= flag; break; case ProgramInformationMergeFieldsFlags.ShortName: if (string.IsNullOrEmpty(mergedProgramInformation.ShortName) || !mergedPriorityFlags.HasFlag(flag)) { mergedProgramInformation.ShortName = otherInformation.ShortName; if (!string.IsNullOrEmpty(mergedProgramInformation.ShortName)) { mergedPriorityFlags |= flag; } } break; case ProgramInformationMergeFieldsFlags.Crcs: var crcsToMerge = otherInformation.Crcs.Where(o => !mergedProgramInformation.Crcs.Any(m => m.Crc == o.Crc)); foreach (var crc in crcsToMerge) { mergedProgramInformation.AddCrc(crc.Crc, crc.Description, crc.Incompatibilities); mergedPriorityFlags |= flag; } break; } } } } return(mergedProgramInformation); }
/// <summary> /// Updates a set of ProgramFeatures to include an incompatibilities set in the given flags. /// </summary> /// <param name="incompatibilityFlags">Condensed incompatibility information.</param> /// <param name="programFeatures">The program features to update.</param> /// <returns>The updated program features.</returns> public static ProgramFeatures ApplyFlagsToProgramFeatures(this IncompatibilityFlags incompatibilityFlags, ProgramFeatures programFeatures) { if ((incompatibilityFlags & IncompatibilityFlags.Ntsc) == IncompatibilityFlags.Ntsc) { programFeatures.Ntsc = FeatureCompatibility.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.Pal) == IncompatibilityFlags.Pal) { programFeatures.Pal = FeatureCompatibility.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.KeyboardComponent) == IncompatibilityFlags.KeyboardComponent) { programFeatures.KeyboardComponent = KeyboardComponentFeatures.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.SuperVideoArcade) == IncompatibilityFlags.SuperVideoArcade) { programFeatures.SuperVideoArcade = FeatureCompatibility.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.SuperVideoArcadeAltered) == IncompatibilityFlags.SuperVideoArcadeAltered) { programFeatures.SuperVideoArcade = FeatureCompatibility.Enhances; } if ((incompatibilityFlags & IncompatibilityFlags.Intellivoice) == IncompatibilityFlags.Intellivoice) { programFeatures.Intellivoice = FeatureCompatibility.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.IntellivisionII) == IncompatibilityFlags.IntellivisionII) { programFeatures.IntellivisionII = FeatureCompatibility.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.IntellivisionIIAltered) == IncompatibilityFlags.IntellivisionIIAltered) { programFeatures.IntellivisionII = FeatureCompatibility.Enhances; } if ((incompatibilityFlags & IncompatibilityFlags.Ecs) == IncompatibilityFlags.Ecs) { programFeatures.Ecs = EcsFeatures.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.Tutorvision) == IncompatibilityFlags.Tutorvision) { programFeatures.Tutorvision = FeatureCompatibility.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.Intellicart) == IncompatibilityFlags.Intellicart) { programFeatures.Intellicart = IntellicartCC3Features.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.CuttleCart3) == IncompatibilityFlags.CuttleCart3) { programFeatures.CuttleCart3 = CuttleCart3Features.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.Jlp) == IncompatibilityFlags.Jlp) { programFeatures.Jlp = JlpFeatures.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.LtoFlash) == IncompatibilityFlags.LtoFlash) { programFeatures.LtoFlash = LtoFlashFeatures.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.Bee3) == IncompatibilityFlags.Bee3) { programFeatures.Bee3 = Bee3Features.Incompatible; } if ((incompatibilityFlags & IncompatibilityFlags.Hive) == IncompatibilityFlags.Hive) { programFeatures.Hive = HiveFeatures.Incompatible; } return(programFeatures); }
/// <summary> /// Expands a set of condensed incompatibility flags to a full-fledged ProgramFeatures set. /// </summary> /// <param name="incompatibilityFlags">The flags to convert.</param> /// <returns>A ProgramFeatures object that includes all incompatibilities.</returns> /// <remarks>The default features are contained in the returned features for any incompatibility flag that is not set.</remarks> public static IProgramFeatures ToProgramFeatures(this IncompatibilityFlags incompatibilityFlags) { var programFeatures = new ProgramFeatures(); return(incompatibilityFlags.ApplyFlagsToProgramFeatures(programFeatures)); }
/// <summary> /// Creates a new instance of the UserSpecifiedProgramInformation class. /// </summary> /// <param name="crc">The CRC of the program's ROM file.</param> /// <param name="title">The title of the program.</param> /// <param name="year">Copyright date of the program.</param> /// <param name="features">The features of the program.</param> /// <param name="crcDescription">Description of the variant of the program identified by its CRC.</param> /// <exception cref="ArgumentException">Thrown if crc is zero.</exception> /// <exception cref="ArgumentNullException">Thrown if any metadata values are <c>null</c>.</exception> public UserSpecifiedProgramInformation(uint crc, string title, string year, ProgramFeatures features, string crcDescription) : this(crc, title, year, features, crcDescription, IncompatibilityFlags.None) { }
/// <summary> /// Creates a new instance of the UserSpecifiedProgramInformation class. /// </summary> /// <param name="crc">The CRC of the program's ROM file.</param> /// <param name="title">The title of the program.</param> /// <param name="year">Copyright date of the program.</param> /// <param name="features">The features of the program.</param> /// <exception cref="ArgumentException">Thrown if crc is zero.</exception> public UserSpecifiedProgramInformation(uint crc, string title, string year, ProgramFeatures features) : this(crc, title, year, features, string.Empty) { }
/// <summary> /// Creates a new instance of the UserSpecifiedProgramInformation class. /// </summary> /// <param name="crc">The CRC of the program's ROM file.</param> /// <param name="title">The title of the program.</param> /// <param name="year">Copyright date of the program.</param> /// <exception cref="ArgumentException">Thrown if crc is zero.</exception> public UserSpecifiedProgramInformation(uint crc, string title, string year) : this(crc, title, year, ProgramFeatures.GetUnrecognizedRomFeatures()) { }
/// <inheritdoc /> public bool Equals(ProgramFeatures other) { return(CompareTo(other) == 0); }
/// <summary> /// Combines two sets of ProgramFeatures into a new set of ProgramFeatures. /// </summary> /// <param name="features1">The first set of features.</param> /// <param name="features2">The second set of features.</param> /// <returns>The combined set of features.</returns> /// <remarks>NOTE: Only basic consistency checks are done. The default behavior is to OR the feature bits of each category /// together, with additional comparison against the default set of bits for the feature set. If the bits are different from /// each other, and one set matches the default bits while the other does not, then the non-default bits "win". /// If both feature sets define a JLP Hardware version, the larger of the two versions is used.</remarks> public static ProgramFeatures Combine(ProgramFeatures features1, ProgramFeatures features2) { ProgramFeatures combinedFeatures = null; if ((features1 == null) && (features2 == null)) { combinedFeatures = GetUnrecognizedRomFeatures(); } else if (features1 == null) { combinedFeatures = features2.Clone(); } else if (features2 == null) { combinedFeatures = features1.Clone(); } else { combinedFeatures = EmptyFeatures.Clone(); foreach (var feature in features1._features.Keys) { var defaultFeatureBits = DefaultFeatures._features[feature]; var features1Bits = features1._features[feature]; var features2Bits = features2._features[feature]; var features1BitsMatchDefault = features1Bits == defaultFeatureBits; var features2BitsMatchDefault = features2Bits == defaultFeatureBits; var featureBits = features1Bits; if (features1BitsMatchDefault && !features2BitsMatchDefault) { featureBits = features2Bits; } else if (features2BitsMatchDefault && !features1BitsMatchDefault) { featureBits = features1Bits; } else if (!features1BitsMatchDefault && !features2BitsMatchDefault) { featureBits = features1Bits | features2Bits; } switch (feature) { case FeatureCategory.Ntsc: case FeatureCategory.Pal: // If we've combined "Tolerates" and "Enhances" we'll get "required", which, for video standards, is not suitable. featureBits = (uint)((FeatureCompatibility)featureBits).CoerceVideoStandardCompatibility(); break; case FeatureCategory.General: case FeatureCategory.SuperVideoArcade: // Leave these alone... break; default: // For these, we don't want to have a 'tolerates' combine with 'enhances' to turn into 'requires', so, if // one set contains only 'tolerates' and the other contains only 'enhances' then just keep 'enhances'. var feature1Compat = features1Bits & FeatureCompatibilityHelpers.CompatibilityMask; var feature2Compat = features2Bits & FeatureCompatibilityHelpers.CompatibilityMask; if ((feature1Compat != 0) && (feature2Compat != 0) && ((feature1Compat ^ feature2Compat) == (uint)FeatureCompatibility.Requires)) { // Retain 'enhances' only. featureBits &= (uint)~FeatureCompatibility.Tolerates; } break; } combinedFeatures.CheckAccessSetFeatureBits(feature, featureBits); } combinedFeatures.JlpHardwareVersion = (JlpHardwareVersion)Math.Max((int)features1.JlpHardwareVersion, (int)features2.JlpHardwareVersion); } return(combinedFeatures); }