/// <summary> /// Checks whether the file is of the desired kind for a program. /// </summary> /// <param name="fileKind">The kind of file to look for.</param> /// <param name="filePath">The file to check.</param> /// <param name="rootFile">When not null or empty, require the file name of the given file path to begin with this value.</param> /// <returns><c>true</c> if the file is a ROM file or a support file for the given root file.</returns> public static bool IsProgramSupportFile(this ProgramFileKind fileKind, string filePath, string rootFile) { // First, check if file is in the blacklist. if (!string.IsNullOrEmpty(filePath) && _romFileBlacklist.Contains(Path.GetFileName(filePath), PathComparer.Instance)) { return(false); } // Now, check if it has appropriate extension. If rootFile is not null or empty, check that FilePath has the same 'base name'. var hasExpectedExtension = fileKind.HasCorrectExtension(filePath); var hasCustomRomExtension = !hasExpectedExtension && fileKind.HasCustomRomExtension(filePath); if (hasCustomRomExtension) { DebugOutput("We have a custom extension!"); } // Don't care about case sensitivity of file system. bool isProgramFile = (hasExpectedExtension || hasCustomRomExtension) && (string.IsNullOrEmpty(rootFile) || Path.GetFileName(filePath).StartsWith(Path.GetFileNameWithoutExtension(rootFile), StringComparison.InvariantCultureIgnoreCase)); if (isProgramFile) { // Check if file name has proper suffix. Don't care about case sensitivity of file system. var suffix = fileKind.GetSuffix(); isProgramFile = string.IsNullOrEmpty(suffix) || Path.GetFileNameWithoutExtension(filePath).EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase); if (!isProgramFile) { // Check if it's in a proper directory. var subdirectoriesForFile = fileKind.GetSubdirectories(); isProgramFile = !subdirectoriesForFile.Any() || subdirectoriesForFile.Contains(Path.GetFileName(Path.GetDirectoryName(filePath)), PathComparer.Instance); } } return(isProgramFile); }
private void UpdateProgramRom(ProgramFileKind whichFile, object data) { if (_programRom == null) { _programRom = new XmlRom(); } var xmlRom = _programRom as XmlRom; switch (whichFile) { case ProgramFileKind.Rom: System.Diagnostics.Debug.Assert(!xmlRom.IsValid, "When is this called on a valid ROM?"); xmlRom.UpdateRomPath(data as string); // ugh... why is this here? ////if (!_supportFiles[ProgramFileKind.Rom].Contains(RomImagePath, System.StringComparer.OrdinalIgnoreCase)) ////{ //// _supportFiles[ProgramFileKind.Rom].Add(RomImagePath); ////} break; case ProgramFileKind.CfgFile: if (!string.IsNullOrEmpty(data as string)) { xmlRom.UpdateConfigPath(data as string); ////if (!_supportFiles[ProgramFileKind.CfgFile].Contains(RomConfigurationFilePath, System.StringComparer.OrdinalIgnoreCase)) ////{ //// _supportFiles[ProgramFileKind.CfgFile].Add(RomConfigurationFilePath); ////} } break; } }
/// <summary> /// Checks whether the file extension is valid for the given program file kind. /// </summary> /// <param name="fileKind">The kind of program file for which the extension is to be validated.</param> /// <param name="filePath">The file path to be checked.</param> /// <returns>If the file has an extension that is valid for the given file kind, returns <c>true</c>; <c>false</c> otherwise.</returns> public static bool HasCorrectExtension(this ProgramFileKind fileKind, string filePath) { var fileTypes = fileKind.FileExtensions(); var extension = GetExtension(filePath); var hasStandardExtension = (extension != null) && fileTypes.Any(e => e.Equals(extension, System.StringComparison.OrdinalIgnoreCase)); return(hasStandardExtension); }
/// <summary> /// Retrieves the current validation state of the given support file. /// </summary> /// <param name="whichFile">Which support file to get a validation state for.</param> /// <returns>The validation state of the file.</returns> public ProgramSupportFileState GetSupportFileState(ProgramFileKind whichFile) { var state = ProgramSupportFileState.None; if (!_supportFileStates.TryGetValue(whichFile, out state)) { state = ProgramSupportFileState.None; } return(state); }
/// <summary> /// Removes the custom extension. /// </summary> /// <param name="fileKind">The file kind for which to remove a custom extension.</param> /// <param name="extension">The custom file extension to no longer associate with the given file kind.</param> public static void RemoveCustomExtension(this ProgramFileKind fileKind, string extension) { if ((fileKind == ProgramFileKind.Rom) && !string.IsNullOrEmpty(extension)) { lock (CustomRomExtensions.Value) { CustomRomExtensions.Value.Remove(extension); } } }
/// <summary> /// Determines if the given file has a custom ROM extension. /// </summary> /// <returns><c>true</c> if the file has a custom rom extension; otherwise, <c>false</c>.</returns> /// <param name="fileKind">File kind.</param> /// <param name="filePath">File path.</param> public static bool HasCustomRomExtension(this ProgramFileKind fileKind, string filePath) { var hasCustomRomExtension = false; if (filePath != null) { if (fileKind == ProgramFileKind.Rom) { var extension = GetExtension(filePath); if (extension == null) { extension = string.Empty; } lock (CustomRomExtensions.Value) { hasCustomRomExtension = CustomRomExtensions.Value.Any(e => e.Equals(extension, System.StringComparison.OrdinalIgnoreCase)); } } } return(hasCustomRomExtension); }
private string SetDefaultSupportFilePath(ProgramFileKind whichFile, string filePath) { string previousValue = null; var files = _supportFiles[whichFile]; bool anyFiles = files.Any(); if (anyFiles) { previousValue = files[0]; } if (string.IsNullOrEmpty(filePath)) { if (anyFiles) { // When the default support file path is set to null, it is removed. // TODO: Should we be setting the path to <null>? The way this works, if // we have more entries, then the second in the list will become the new // default, which is not intuitive. files.RemoveAt(0); } } else { var existingMatch = files.FirstOrDefault(f => System.StringComparer.OrdinalIgnoreCase.Compare(f, filePath) == 0); if (existingMatch != null) { files.Remove(existingMatch); files.Insert(0, filePath); } else if (anyFiles) { files[0] = filePath; } else { files.Insert(0, filePath); } } return(previousValue); }
/// <summary> /// Shows a dialog to select a support file for the program. /// </summary> /// <param name="program">The program for which a support file is being selected.</param> /// <param name="kind">The kind of support file to browse for.</param> /// <returns>The selected support file.</returns> internal static string BrowseForSupportFile(ProgramViewModel program, ProgramFileKind kind) { string filter = null; string prompt = null; switch (kind) { case ProgramFileKind.ManualText: filter = RomListViewModel.SelectManualFilter; prompt = string.Format(System.Globalization.CultureInfo.CurrentCulture, RomListViewModel.SelectManualPromptFormat, program.ProgramDescription.Name); break; case ProgramFileKind.SaveData: filter = RomListViewModel.SelectJlpSaveDataFilter; prompt = string.Format(System.Globalization.CultureInfo.CurrentCulture, RomListViewModel.SelectJlpSavePromptFormat, program.ProgramDescription.Name); break; default: ErrorReporting.ReportNotImplementedError("ProgramViewModel.BrowseForSupportFile"); break; } string supportFilePath = null; var fileBrowser = FileDialogHelpers.Create(); fileBrowser.IsFolderBrowser = false; fileBrowser.AddFilter(filter, kind.FileExtensions()); fileBrowser.AddFilter(FileDialogHelpers.AllFilesFilter, new string[] { ".*" }); fileBrowser.Title = prompt; fileBrowser.EnsureFileExists = true; fileBrowser.EnsurePathExists = true; fileBrowser.Multiselect = false; var result = fileBrowser.ShowDialog(); if (result == FileBrowserDialogResult.Ok) { supportFilePath = fileBrowser.FileNames.First(); } return(supportFilePath); }
private ProgramSupportFileState GetSupportFileState(ProgramFileKind kind) { var state = ProgramSupportFileState.None; switch (LtoFlashViewModel.SyncMode) { case MenuLayoutSynchronizationMode.None: break; case MenuLayoutSynchronizationMode.RomList: if (ProgramDescription != null) { state = ProgramDescription.Files.GetSupportFileState(kind); } break; case MenuLayoutSynchronizationMode.ToLtoFlash: case MenuLayoutSynchronizationMode.FromLtoFlash: state = State; break; } return(state); }
public void ProgramFileKind_HasCorrectExtensionForUnsupportedFileKind_ThrowsKeyNotFoundException(ProgramFileKind fileKind) { Assert.Throws <KeyNotFoundException>(() => fileKind.HasCorrectExtension(@"I:\ntellivision\Rules.forever")); }
public void ProgramFileKind_HasCorrectExtension_ReturnsCorrectFileExtensions(ProgramFileKind fileKind, string filePath, bool expectedHasCorrectExtension) { Assert.Equal(expectedHasCorrectExtension, fileKind.HasCorrectExtension(filePath)); }
public void ProgramFileKind_FileExtensionForUnsupportedFileKind_ThrowsKeyNotFoundException(ProgramFileKind fileKind) { Assert.Throws <KeyNotFoundException>(() => fileKind.FileExtension()); }
public void ProgramFileKind_FileExtension_ReturnsCorrectFileExtension(ProgramFileKind fileKind, string expectedFileExtension) { Assert.Equal(expectedFileExtension, fileKind.FileExtension()); }
public void ProgramFileKind_FileExtensions_ReturnsCorrectFileExtensions(ProgramFileKind fileKind, IEnumerable <string> expectedFileExtensions) { AssertCollectionsAreEquivalent(expectedFileExtensions, fileKind.FileExtensions()); }
public void ProgramFileKind_GetSubdirectoriesForUnsupportedFileKind_ThrowsKeyNotFoundException(ProgramFileKind fileKind) { Assert.Throws <KeyNotFoundException>(() => fileKind.GetSubdirectories()); }
/// <summary> /// Get the suffix to append to a file name for a particular <see cref="ProgramFileKind"/>, such as a manual or overlay. /// </summary> /// <param name="fileKind">The kind of program file for which a suffix is desired.</param> /// <returns>The suffix to use. May be empty.</returns> /// <exception cref="KeyNotFoundException">Thrown if <paramref name="fileKind"/> does not support a suffix.</exception> public static string GetSuffix(this ProgramFileKind fileKind) { return(FileSuffixForFileKind.Value[fileKind]); }
public void ProgramFileKind_GetSuffix_ReturnsCorrectSuffix(ProgramFileKind fileKind, string expectedSuffix) { Assert.Equal(expectedSuffix, fileKind.GetSuffix()); }
/// <summary> /// Checks whether a file path is a program ROM file. /// </summary> /// <param name="fileKind">The kind of program file to check.</param> /// <param name="filePath">The file path to inspect to determine if it is of the indicated program file kind.</param> /// <returns><c>true</c> if the given file is of the given type.</returns> public static bool IsProgramFile(this ProgramFileKind fileKind, string filePath) { var isProgram = fileKind.IsProgramSupportFile(filePath, null); return(isProgram); }
/// <summary> /// Unconditionally adds a support file to the support files associated with a program. /// </summary> /// <param name="whichFile">Which support file to add.</param> /// <param name="filePath">Absolute path of the support file being added.</param> public void AddSupportFile(ProgramFileKind whichFile, string filePath) { _supportFiles[whichFile].Add(filePath); }
/// <summary> /// Gets the default file extension for a program file kind. /// </summary> /// <param name="fileKind">The kind of program file for which an extension is desired.</param> /// <returns>The file extension.</returns> /// <exception cref="KeyNotFoundException">Thrown if <paramref name="fileKind"/> is not supported.</exception> public static string FileExtension(this ProgramFileKind fileKind) { return(fileKind.FileExtensions().First()); }
/// <summary> /// Get the file extension for a program file. /// </summary> /// <param name="fileKind">The kind of program file for which an extension is desired.</param> /// <returns>The file extensions. May be empty.</returns> /// <exception cref="KeyNotFoundException">Thrown if <paramref name="fileKind"/> is not supported.</exception> public static IEnumerable <string> FileExtensions(this ProgramFileKind fileKind) { return(FileExtensionsForFileKind.Value[fileKind]); }
/// <summary> /// Get the subdirectories into which to store a support file for a particular <see cref="ProgramFileKind"/>, such as a manual or overlay. /// </summary> /// <param name="fileKind">The kind of program file for which a subdirectory is desired.</param> /// <returns>The subdirectories that may be used. File kinds that refer to known ROM types, or are unrecognized, will return an empty enumerable.</returns> /// <exception cref="KeyNotFoundException">Thrown if <paramref name="fileKind"/> is not valid.</exception> public static IEnumerable <string> GetSubdirectories(this ProgramFileKind fileKind) { return(FileSubdirectoriesForFileKind.Value[fileKind]); }
public void ProgramFileKind_HasCustomRomExtensionForNullPath_ReturnsFalse(ProgramFileKind fileKind) { Assert.False(fileKind.HasCustomRomExtension(null)); }
public void ProgramFileKind_GetSubdirectories_ReturnsCorrectSubdirectories(ProgramFileKind fileKind, IEnumerable <string> expectedSubdirecories) { AssertCollectionsAreEquivalent(expectedSubdirecories, fileKind.GetSubdirectories()); }
public void ProgramFileKind_HasCustomRomExtensionForEmptyPath_ReturnsFalseExceptForRomKind(ProgramFileKind fileKind) { var expectedResult = fileKind == ProgramFileKind.Rom; Assert.Equal(expectedResult, fileKind.HasCustomRomExtension(string.Empty)); }
/// <summary> /// Updates the validation state of the given support file. /// </summary> /// <param name="whichFile">Which file to validate.</param> /// <param name="crc">If non-zero, the CRC of the program ROM to compare against if validating the ROM file.</param> /// <param name="programDescription">The program description being validated (if applicable).</param> /// <param name="peripherals">The peripherals attached to the system, used for compatibility checks.</param> /// <param name="connectedPeripheralsHistory">The peripherals that have been attached to the system over time.</param> /// <param name="reportIfModified">If <c>true</c>, check if the file has been modified, not just whether it exists.</param> /// <returns>The updated state of the file.</returns> public ProgramSupportFileState ValidateSupportFile(ProgramFileKind whichFile, uint crc, IProgramDescription programDescription, IEnumerable <IPeripheral> peripherals, IEnumerable <IPeripheral> connectedPeripheralsHistory, bool reportIfModified) { var validationState = ProgramSupportFileState.None; switch (whichFile) { case ProgramFileKind.Rom: var isValid = Rom.Validate(); if (!string.IsNullOrEmpty(RomImagePath)) { var previousValidationState = ProgramSupportFileState.None; _supportFileStates.TryGetValue(whichFile, out previousValidationState); if (!StreamUtilities.FileExists(RomImagePath)) { validationState = ProgramSupportFileState.Missing; if (AlternateRomImagePaths.Any(p => StreamUtilities.FileExists(p))) { validationState = ProgramSupportFileState.MissingWithAlternateFound; } } else if (reportIfModified) { isValid = _programRom.IsValid; if (crc != 0) { // In some cases, the CRC provided is actually Rom.Crc, so if they match, recompute the CRC. var cfgCrc = 0u; isValid = (Rom.Crc == crc) && (crc == GetRefreshedCrcForRom(RomImagePath, RomConfigurationFilePath, out cfgCrc) && (Rom.CfgCrc == cfgCrc)); } validationState = isValid ? ProgramSupportFileState.PresentAndUnchanged : ProgramSupportFileState.PresentButModified; } switch (validationState) { case ProgramSupportFileState.PresentAndUnchanged: case ProgramSupportFileState.None: // Treat a ROM file's missing or modified state as higher priority to report than peripheral-related information. // This bit of code is entirely LTO Flash!-specific in its assumptions. If there should ever be other // peripheral-specific needs to address here, a larger architectural change may be necessary. While the // language of the states here is neutral, the basis of this check is not. var rom = programDescription == null ? Rom : programDescription.Rom; var requiresPeripheral = rom.IsLtoFlashOnlyRom(); if (requiresPeripheral) { var isUniversallyCompatible = rom.GetTargetDeviceUniqueId() == LuigiScrambleKeyBlock.AnyLTOFlashId; var matchesPeripheralInDeviceHistory = isUniversallyCompatible || ((connectedPeripheralsHistory != null) && (connectedPeripheralsHistory.FirstOrDefault(p => p.IsRomCompatible(programDescription)) != null)); var canRunOnConnected = isUniversallyCompatible || ((peripherals != null) && (peripherals.FirstOrDefault(p => p.IsRomCompatible(programDescription)) != null)); if (peripherals == null) { // If previous validation state was due to peripheral, retain it, since we don't // have any peripherals to check against. if (validationState != previousValidationState) { switch (previousValidationState) { case ProgramSupportFileState.RequiredPeripheralAvailable: case ProgramSupportFileState.RequiredPeripheralIncompatible: case ProgramSupportFileState.RequiredPeripheralNotAttached: case ProgramSupportFileState.RequiredPeripheralUnknown: validationState = previousValidationState; break; case ProgramSupportFileState.None: validationState = matchesPeripheralInDeviceHistory ? ProgramSupportFileState.RequiredPeripheralNotAttached : ProgramSupportFileState.RequiredPeripheralUnknown; break; default: // TODO: Decide if the following is a bug: // 0: Presume a scrambled (unique) LUIGI, no device or device history provided (null) // 1. Initially ROM's file is missing, but its alternate is found - this caches the 'MissingButAlternateFound' state // 2. Update ROM to use alternate path as primary path // 3. Re-validate // At this point, the "Present and unmodified" state is used -- despite the ROM requiring // a specific device. // Why is this considered correct at this time? // a) When no devices or device history are give (nulls), it's impossible to know. So just use the simple state of he file. // b) It MAY be a bug that, if we pass in EMPTY peripheral / history lists that we should consider something different... but // then again, should we report 'unknown peripheral' at that time? Or would reporting 'not attached' be better? // What about 'universally' scrambled ROMs? Using 'not attached' may be more accurate then as well... // The case of scrambled ROMs likely needs more careful consideration generally... break; } } } else { if (peripherals.Any()) { if (canRunOnConnected) { validationState = ProgramSupportFileState.RequiredPeripheralAvailable; } else { validationState = ProgramSupportFileState.RequiredPeripheralIncompatible; } } else { validationState = matchesPeripheralInDeviceHistory ? ProgramSupportFileState.RequiredPeripheralNotAttached : ProgramSupportFileState.RequiredPeripheralUnknown; } } } break; default: break; } } break; default: // TODO: Implement remaining validation code. break; } _supportFileStates[whichFile] = validationState; return(validationState); }