/// <summary> /// Compare two ROM instances. /// </summary> /// <param name="x">The first ROM to compare.</param> /// <param name="programInformationRomX">Additional information about the <paramref name="x"/> ROM.</param> /// <param name="y">The second ROM to compare.</param> /// <param name="programInformationRomY">Additional information about the <paramref name="y"/> ROM.</param> /// <returns>If the two ROMs are considered equal, zero; if <paramref name="x"/> is considered 'greater than' <paramref name="y"/>, a positive number; /// if <paramref name="x"/> is considered 'less than' <paramref name="y"/>, a negative number.</returns> public abstract int Compare(IRom x, IProgramInformation programInformationRomX, IRom y, IProgramInformation programInformationRomY);
public void IProgramInformation_GetNameFOrCrcOnNullInformation_ThrowsNullReferenceException() { IProgramInformation information = null; Assert.Throws <NullReferenceException>(() => information.GetNameForCrc(1u)); }
/// <summary> /// Add a new CRC value to the program information. /// </summary> /// <param name="programInformation">The program information to which a new CRC is to be added.</param> /// <param name="newCrc">The new CRC by which the program may be identified.</param> /// <returns><c>true</c> if the new Crc value was added, <c>false</c> if the Crc was already present.</returns> /// <remarks>No description or incompatibility flags will be assigned. It is assumed the ROM is compatible with all Intellivision hardware /// and needs no special identification.</remarks> public static bool AddCrc(this IProgramInformation programInformation, uint newCrc) { return(programInformation.AddCrc(newCrc, string.Empty)); }
/// <summary> /// Add a new CRC value to the program information. /// </summary> /// <param name="programInformation">The program information to which a new CRC is to be added.</param> /// <param name="newCrc">The new CRC by which the program may be identified.</param> /// <param name="crcDescription">A brief (one or two word) description, if applicable.</param> /// <returns><c>true</c> if the new Crc value was added, <c>false</c> if the Crc was already present.</returns> /// <remarks>No incompatibility flags will be assigned. It is assumed the ROM is compatible with all Intellivision hardware /// and needs no special identification.</remarks> public static bool AddCrc(this IProgramInformation programInformation, uint newCrc, string crcDescription) { return(programInformation.AddCrc(newCrc, crcDescription, IncompatibilityFlags.None)); }
/// <inheritdoc /> public override int Compare(IRom x, IProgramInformation programInformationRomX, IRom y, IProgramInformation programInformationRomY) { var result = base.Compare(x, programInformationRomX, y, programInformationRomY); if ((result == 0) && (x != null)) { switch (x.Format) { case RomFormat.Bin: result = CheckCrcs(x, programInformationRomX, y, programInformationRomY); break; case RomFormat.Luigi: result = CheckCrcs(x, programInformationRomX, y, programInformationRomY); if ((result == 0) && (y.Format == RomFormat.Luigi)) { // It's possible LUIGI files' original CRCs match, but the LUIGI files themselves do not. // This once occurred when additional bytes were somehow appended to a downloaded LUIGI. In // that case, all the CRC data matched, so even the "strict" CRC check, which compared the // full 8 bytes of the ID in the header, still matched. However, the additional data at the // end of the corrupted file resulted in failure to load on LTO Flash! hardware. Therefore, // when comparing what appear to be two identical LUIGI files, still compare full file CRCs. result = (int)(Rom.AsSpecificRomType <LuigiFormatRom>(x).Crc24 - Rom.AsSpecificRomType <LuigiFormatRom>(y).Crc24); } break; default: break; } } return(result); }
private IProgramFeatures GetInitialProgramFeatures(IProgramDescription programDescription, IProgramInformation programInformation) { IProgramFeatures initialProgramFeatures = null; if (programDescription != null) { initialProgramFeatures = programDescription.Features; } if ((initialProgramFeatures == null) && (programInformation != null)) { initialProgramFeatures = programInformation.Features; } return(initialProgramFeatures); }
public void AddProgram(IProgramInformation programInformation) { _programs.Add(programInformation); }
public void IProgramInformation_AddCrcWithDescriptionToNullInformation_ThrowsNullReferenceException() { IProgramInformation information = null; Assert.Throws <NullReferenceException>(() => information.AddCrc(1u, "description")); }
/// <inheritdoc /> public override int Compare(IRom x, IProgramInformation programInformationRomX, IRom y, IProgramInformation programInformationRomY) { var result = BaseComparer.Compare(x, programInformationRomX, y, programInformationRomY); if (result != 0) { result = CanonicalCompare(x, y, false); } return(result); }
/// <summary> /// Gets program information from a ROM. /// </summary> /// <param name="rom">The ROM from which to retrieve information.</param> /// <returns>The program information.</returns> /// <remarks>Program information will be honored according to the following order or precedence rules: /// 1. Program databases /// 2. ROM-specific metadata /// a. LUIGI metadata check /// b. ROM ID tag metadata check /// c. CFGVAR metadata check /// 3. intvname utility check (which has its own internal order-of-precedence, likely overlapping with this) /// If multiple sources are available, an attempt is made to merge the results.</remarks> public static IProgramInformation GetProgramInformation(this IRom rom) { // NOTE: ROM database is still CRC-based, and does not use a RomComparer! var programInfo = ProgramInformationTable.Default.FindProgram(rom.Crc); rom.EnsureCfgFileProvided(programInfo); IProgramInformation metadataProgramInfo = rom.GetLuigiFileMetadata(); if (metadataProgramInfo == null) { metadataProgramInfo = rom.GetRomFileMetadata(); } if (metadataProgramInfo == null) { metadataProgramInfo = rom.GetBinFileMetadata(); } var programInfoData = rom.GetRomInformation(); var programName = programInfoData.GetRomInfoString(RomInfoIndex.Name); var programYear = programInfoData.GetRomInfoString(RomInfoIndex.Copyright); var programShortName = programInfoData.GetRomInfoString(RomInfoIndex.ShortName); var intvNameInfo = new UserSpecifiedProgramInformation(rom.Crc, programName, programYear, rom.GetProgramFeatures()); if (programInfoData.Any(s => !string.IsNullOrEmpty(s))) { if (!string.IsNullOrEmpty(programShortName)) { intvNameInfo.ShortName = programShortName; } if (programInfo != null) { // If we have a database entry, which is the ultimate arbiter of "known" -- either a-priory, or as specified // explicitly by the user as a new entry for the database, then strip the "unrecognized-ness" from the intvname // data. Presumably, the database entry specified these values. We do not want to re-mark the features as unknown // when merging intvname information into a database entry's information. The ROM metadata that can be directly // harvested from a program, however, does not track these aspects of a program, hence we will retain the values // in the situation where we may have nearly complete feature data about a program aside from what the database tracks. intvNameInfo.Features.ClearUnrecongizedRomFeatures(); } } else if ((programInfo != null) || (metadataProgramInfo != null)) { intvNameInfo = null; } if ((programInfo == null) && (metadataProgramInfo != null)) { // Mark features with unrecognized settings, since we don't have a database entry. metadataProgramInfo.Features.SetUnrecongizedRomFeatures(); } var primaryInfo = programInfo ?? metadataProgramInfo ?? intvNameInfo; var secondaryInfo = object.ReferenceEquals(primaryInfo, metadataProgramInfo) ? intvNameInfo : metadataProgramInfo ?? intvNameInfo; var tertiaryInfo = object.ReferenceEquals(secondaryInfo, intvNameInfo) ? null : intvNameInfo; if (secondaryInfo != null) { if (tertiaryInfo != null) { programInfo = primaryInfo.Merge( ProgramInformationMergeFieldsFlags.All, new Tuple <IProgramInformation, ProgramInformationMergeFieldsFlags>(secondaryInfo, ProgramInformationMergeFieldsFlags.All), new Tuple <IProgramInformation, ProgramInformationMergeFieldsFlags>(tertiaryInfo, ProgramInformationMergeFieldsFlags.All)); } else { programInfo = primaryInfo.Merge( ProgramInformationMergeFieldsFlags.All, new Tuple <IProgramInformation, ProgramInformationMergeFieldsFlags>(secondaryInfo, ProgramInformationMergeFieldsFlags.All)); } } else { programInfo = primaryInfo; } return(programInfo); }
/// <summary> /// Given a list of files and / or directories, enumerate what appear to be valid program ROM files. /// </summary> /// <param name="files">A list of files and / or directories to inspect to locate ROM files.</param> /// <param name="acceptCancellation">Delegate to call to determine if the operation should accept being cancelled by the user.</param> /// <param name="progressFunc">Delegate to call to update progress of the operation.</param> /// <returns>An enumerable of valid program ROM files.</returns> public static IEnumerable <IRom> IdentifyRomFiles(this IEnumerable <string> files, Func <bool> acceptCancellation, Action <string> progressFunc) { var potentialRomFilePaths = GetPotentialProgramRomFiles(files, acceptCancellation, progressFunc).Where(f => ProgramFileKind.Rom.IsProgramFile(f)); foreach (var romFilePath in potentialRomFilePaths) { if (acceptCancellation()) { yield break; } if (progressFunc != null) { progressFunc(romFilePath); } var configFilePath = INTV.Shared.Model.Program.ProgramFileKindHelpers.GetConfigFilePath(romFilePath); if (!string.IsNullOrEmpty(configFilePath) && File.Exists(configFilePath)) { INTV.Core.Model.IRom programRom = null; try { programRom = INTV.Core.Model.Rom.Create(romFilePath, configFilePath); } catch (IOException) { // TODO: Report error here -- this has been found to occur of scanning SkyDrive / OneDrive in Windows 8.1. } if (programRom != null) { yield return(programRom); } } else { INTV.Core.Model.IRom programRom = null; try { programRom = INTV.Core.Model.Rom.Create(romFilePath, null); } catch (IOException) { // TODO: Report error here -- this has been found to occur of scanning SkyDrive / OneDrive in Windows 8.1. } if (programRom != null) { yield return(programRom); } else { // Might be a lone 'bin' file, such as EXEC or GROM. Run a checksum. This will result in a second checksum later, but oh, well. IProgramInformation programInfo = null; try { var crc = INTV.Core.Utility.Crc32.OfFile(romFilePath); programInfo = ProgramInformationTable.Default.FindProgram(crc); } catch (IOException) { // TODO: Report error here -- this has been found to occur of scanning SkyDrive / OneDrive in Windows 8.1. } if (programInfo != null) { if (INTV.Core.Model.Rom.CheckRomFormat(romFilePath) != RomFormat.None) { yield return(INTV.Core.Model.Rom.Create(romFilePath, null)); } } } } } }
public void IProgramInformationToXmlRomInformationConverter_ConvertNullInformation_ThrowsNullReferenceException() { IProgramInformation information = null; Assert.Throws <NullReferenceException>(() => IProgramInformationToXmlRomInformationConverter.Instance.Convert(information).Any()); }
private IProgramMetadata GetInitialProgramMetadatda(IProgramDescription programDescription, IProgramInformation programInformation) { IProgramMetadata initialProgramMetadata = null; if ((programDescription != null) && (programDescription.Rom != null)) { initialProgramMetadata = programDescription.Rom.GetProgramMetadata(); } if ((initialProgramMetadata == null) && (programInformation != null)) { // Several implementations of IProgramInformation also implement IProgramMetadata. initialProgramMetadata = programInformation as IProgramMetadata; } return(initialProgramMetadata); }
public void IProgramInformation_GetDatabaseCodeOnNullInformation_ReturnsNullCode() { IProgramInformation information = null; Assert.Null(information.GetDatabaseCode()); }
/// <inheritdoc /> public override int Compare(IRom x, IProgramInformation programInformationX, IRom y, IProgramInformation programInformationY) { var result = BaseComparer.Compare(x, programInformationX, y, programInformationY); if (result != 0) { result = CanonicalCompare(x, y, true); // require features to match as well } return(result); }
public void IProgramInformation_MergeNullInformatoin_ThrowsArgumentException() { IProgramInformation information = null; Assert.Throws <ArgumentException>(() => information.Merge(ProgramInformationMergeFieldsFlags.None)); }
public void UserSpecifiedProgramInformation_ConstructWithEmptyMetadataProgramInformationWithNullMetadataFields_ThrowsArgumentNullException(IProgramInformation sourceInformation) { Assert.Throws <ArgumentNullException>(() => new UserSpecifiedProgramInformation(sourceInformation)); }
public void IProgramInformation_ModifyCrcOnNullInformation_ThrowsNullReferenceException() { IProgramInformation information = null; Assert.Throws <NullReferenceException>(() => information.ModifyCrc(1u, "description", IncompatibilityFlags.Ntsc)); }
/// <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); }
private static int CheckCrcs(IRom x, IProgramInformation programInformationRomX, IRom y, IProgramInformation programInformationRomY) { var cfgCrcRomX = x.CfgCrc; var cfgCrcRomY = y.CfgCrc; var result = (int)cfgCrcRomX - (int)cfgCrcRomY; if ((result != 0) && ((cfgCrcRomX == 0) || (cfgCrcRomY == 0))) { if (cfgCrcRomX == 0) { var programInfo = programInformationRomX; if (programInfo == null) { programInfo = x.GetProgramInformation(); } var cfgFilePath = x.GetStockCfgFile(programInfo); if (!string.IsNullOrEmpty(cfgFilePath)) { cfgCrcRomX = INTV.Core.Utility.Crc32.OfFile(cfgFilePath); } } if (cfgCrcRomY == 0) { var programInfo = programInformationRomY; if (programInfo == null) { programInfo = y.GetProgramInformation(); } var cfgFilePath = y.GetStockCfgFile(programInfo); if (!string.IsNullOrEmpty(cfgFilePath)) { cfgCrcRomY = INTV.Core.Utility.Crc32.OfFile(cfgFilePath); } } result = (int)cfgCrcRomX - (int)cfgCrcRomY; } return(result); }
/// <summary> /// Adds an entry to the table if it does not already exist. /// </summary> /// <param name="programInfo">The program information to add.</param> /// <returns><c>true</c> if the entry was added, otherwise <c>false</c>.</returns> public bool AddEntry(IProgramInformation programInfo) { throw new System.NotImplementedException(Resources.Strings.ProgramInformationTable_NoEditsToDefaultDatabase); }