/// <summary>
        /// Add a new entry to the information table.
        /// </summary>
        /// <param name="program">The program to add to the table.</param>
        /// <returns><c>true</c> if the program was added. If it was not, then a match based on CRC was found.</returns>
        public bool AddEntry(UserSpecifiedProgramInformation program)
        {
            // Check the incoming program's CRCs against the CRCs of existing entries. If any CRC in the incoming program matches a CRC of an entry, then  it's a match!
            // TODO: Move to also include CfgCrc where appropriate.  Ideally we would canonicalize CFG file content in memory and then check.
            var  existingEntry = _programs.FirstOrDefault(e => e.Crcs.FirstOrDefault(c => program.Crcs.FirstOrDefault(crc => crc.Crc == c.Crc) != null) != null);
            bool addedEntry    = existingEntry == null;

            if (addedEntry)
            {
                _programs.Add(program);
            }
            return(addedEntry);
        }
示例#2
0
        /// <summary>
        /// Initializes a new instance of ProgramDescription.
        /// </summary>
        /// <param name="crc">The CRC of the program.</param>
        /// <param name="rom">The ROM file or files.</param>
        /// <param name="programInfo">Additional information about the program.</param>
        public ProgramDescription(uint crc, IRom rom, IProgramInformation programInfo)
        {
            _crc         = crc;
            _rom         = rom;
            _programInfo = new UserSpecifiedProgramInformation(programInfo);
            _xmlVendor   = new MetadataString()
            {
                Text = _programInfo.Vendor
            };
            _name    = _programInfo.GetNameForCrc(crc);
            _xmlName = new MetadataString()
            {
                Text = _name
            };
            _shortName    = programInfo.ShortName;
            _xmlShortName = new MetadataString()
            {
                Text = _shortName
            };
            _programFiles = new ProgramSupportFiles(rom);
            var allIncompatibilities = _programInfo.Crcs.Select(c => c.Incompatibilities);
            var crcData = _programInfo.Crcs.First(c => c.Crc == crc);
            var romSpecificIncompatibilities = crcData.Incompatibilities;

            if ((romSpecificIncompatibilities != IncompatibilityFlags.None) || (allIncompatibilities.Distinct().Count() > 1))
            {
                var incompatibilities = _programInfo.Features.ToIncompatibilityFlags();
                if (romSpecificIncompatibilities != incompatibilities)
                {
                    _features = romSpecificIncompatibilities.ApplyFlagsToProgramFeatures(_programInfo.Features.Clone());
                }
                else
                {
                    _features = programInfo.Features;
                }
            }
            else
            {
                _features = programInfo.Features;
            }
        }
        /// <summary>
        /// Group an entry with an existing one in the table.
        /// </summary>
        /// <param name="program">The program to be combined with another in the table.</param>
        /// <param name="groupWithEntry">The entry to group with.</param>
        /// <returns><c>true</c> if the new entry was merged into the existing one.</returns>
        public bool GroupWithExistingEntry(UserSpecifiedProgramInformation program, UserSpecifiedProgramInformation groupWithEntry)
        {
            // TODO: Improvement should be done regarding CFG CRCs.
            var newCrcs = Enumerable.Empty <CrcData>();

            if (_programs.Contains(groupWithEntry))
            {
                var groupEntryCrcs = groupWithEntry.Crcs.Select(c => c.Crc).ToList();
                newCrcs = program.Crcs.Where(c => !groupEntryCrcs.Contains(c.Crc));
            }
            var groupedWithEntry = newCrcs.Any();

            if (groupedWithEntry)
            {
                foreach (var crc in newCrcs)
                {
                    groupWithEntry.AddCrc(crc.Crc, crc.Description, crc.Incompatibilities);
                }
            }
            return(groupedWithEntry);
        }
示例#4
0
        /// <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);
        }