private static uint GetCrcs(LuigiFileHeader header, string romPath, string cfgPath, out uint cfgCrc, out bool usedLuigiFileCrc) { usedLuigiFileCrc = false; uint romCrc = 0; cfgCrc = 0; if (header != null) { if (header.Version > 0) { if ((header.OriginalRomFormat == RomFormat.Bin) || (header.OriginalRomFormat == RomFormat.Rom)) { romCrc = header.OriginalRomCrc32; // Report the original file's CRC to help identify the ROM. } if (header.OriginalRomFormat == RomFormat.Bin) { cfgCrc = header.OriginalCfgCrc32; } } } if (romCrc == 0 && StreamUtilities.FileExists(romPath)) { usedLuigiFileCrc = true; romCrc = INTV.Core.Utility.Crc32.OfFile(romPath); } return(romCrc); }
/// <inheritdoc /> public override bool Validate() { IsValid = !string.IsNullOrEmpty(RomPath); if (IsValid) { IsValid = StreamUtilities.FileExists(RomPath); } return(IsValid); }
public void StreamUtilitiesWithTestStorage_CallOpenFileStream_ReturnsValidStream() { var storage = new TestStorageAccess(); var testPath = @"~/open_create_path.dat"; using (var stream = StreamUtilities.OpenFileStream(testPath, storage)) { Assert.True(StreamUtilities.FileExists(testPath, storage)); Assert.NotNull(stream); } Assert.False(StreamUtilities.FileExists(testPath, storage)); }
public void StreamUtilities_InitializeCheckDefaultAndRemoveStorage_Succeeds() { var storageAcces = new MyPrivateStorageAccess(); // We use a privately defined type for the storage access to check initialize and remove, which will // also guarantee that there is a default storage access at least for the duration of this test. Assert.True(StreamUtilities.Initialize(storageAcces)); Assert.False(StreamUtilities.Initialize(storageAcces)); Assert.NotNull(StreamUtilities.DefaultStorageAccess); Assert.False(StreamUtilities.FileExists(@"~-=</.\/_\/.\>=-~")); Assert.True(StreamUtilities.Remove(storageAcces)); Assert.False(StreamUtilities.Remove(storageAcces)); }
/// <summary> /// Get the Crc32 values of a ROM. /// </summary> /// <param name="romPath">Absolute path of the ROM whose CRC32 value is desired.</param> /// <param name="cfgPath">Absolute path of the configuration (.cfg) file whose CRC32 is desired. May be <c>null</c>, depending on the ROM.</param> /// <param name="cfgCrc">Receives the CRC32 of the configuration file, if applicable. Could be zero.</param> /// <returns>CRC32 of the ROM file.</returns> /// <remarks>Instead of zero, should the Crc32.InitialValue be used as a sentinel 'invalid' value?</remarks> internal static uint GetCrcs(string romPath, string cfgPath, out uint cfgCrc) { cfgCrc = 0; uint romCrc = 0; if (!string.IsNullOrEmpty(romPath) && StreamUtilities.FileExists(romPath)) { romCrc = Crc32.OfFile(romPath); } if (!string.IsNullOrEmpty(cfgPath) && StreamUtilities.FileExists(cfgPath)) { cfgCrc = Crc32.OfFile(cfgPath); } return(romCrc); }
/// <summary> /// Gets the ROM to use from a <see cref="ProgramDescription"/> for deployment to a device, e.g. Intellicart, CC3, LTO Flash!, emulator, et. al. /// </summary> /// <param name="programDescription">A <see cref="ProgramDescription"/> that wraps a specific <see cref="IRom"/>.</param> /// <returns>The specific <see cref="IRom"/> to use for deployment.</returns> /// <remarks>A specific <see cref="IRom"/> may refer to a file that is not presently accessible, e.g. a ROM on a CD-ROM drive, network volume, or other /// non-fixed-disk location. In such a case, if the <paramref name="programDescription"/> supplies alternative location(s), the first suitable alternative /// location that can be accessed will be used to return a viable <see cref="IRom"/> that, hopefully, is the equivalent of the original.</remarks> public static IRom GetRom(this ProgramDescription programDescription) { var rom = programDescription.Rom; var usesCfg = !string.IsNullOrEmpty(rom.ConfigPath); if (!StreamUtilities.FileExists(rom.RomPath) || (usesCfg && !StreamUtilities.FileExists(rom.ConfigPath))) { var alternateRomPaths = programDescription.Files.AlternateRomImagePaths.ToList(); var alternateCfgPaths = programDescription.Files.AlternateRomConfigurationFilePaths.ToList(); if (usesCfg && (alternateRomPaths.Count != alternateCfgPaths.Count)) { throw new InvalidOperationException(Resources.Strings.ProgramDescription_MissingAlternateCfgFile); } var foundAlternate = false; string romPath = null; string cfgPath = null; for (var i = 0; (i < alternateRomPaths.Count) && !foundAlternate; ++i) { if (StreamUtilities.FileExists(alternateRomPaths[i])) { romPath = alternateRomPaths[i]; if (usesCfg) { if (StreamUtilities.FileExists(alternateCfgPaths[i])) { // This code assumes (but cannot check -- silly PCL has no Path API) that the .cfg and ROM are in the same directory for the same index. cfgPath = alternateCfgPaths[i]; foundAlternate = true; } } else { foundAlternate = true; } } } if (foundAlternate) { rom = new AlternateRom(romPath, cfgPath, rom); } } return(rom); }
/// <inheritdoc /> public override bool Validate() { var isValid = !string.IsNullOrEmpty(RomPath); if (isValid) { isValid = StreamUtilities.FileExists(RomPath); } if (isValid) { if (!string.IsNullOrEmpty(ConfigPath)) { isValid = StreamUtilities.FileExists(ConfigPath); } } IsValid = isValid; return(IsValid); }
/// <summary> /// Locates the first data block of the requested type in the ROM. /// </summary> /// <typeparam name="T">The type of LUIGI block to locate.</typeparam> /// <returns>The data block, or <c>null</c> if no block of the requested type is in the ROM.</returns> internal T LocateDataBlock <T>() where T : LuigiDataBlock { var dataBlock = default(T); if (StreamUtilities.FileExists(RomPath)) { using (var file = StreamUtilities.OpenFileStream(RomPath)) { if (file != null) { if (file.Length > 0) { var desiredBlockType = LuigiDataBlock.GetBlockType <T>(); var luigiHeader = LuigiFileHeader.Inflate(file); var bytesRead = luigiHeader.DeserializeByteCount; // Start looking for desired block immediately after header. var block = LuigiDataBlock.Inflate(file); bytesRead += block.DeserializeByteCount; if (StopIfScrambleKeyBlockFound(desiredBlockType, block.Type)) { // Stop looking. If we hit the scramble key, there's nothing more to be looked at. bytesRead = (int)file.Length; } while ((bytesRead < file.Length) && (block.Type != desiredBlockType) && (block.Type != LuigiDataBlockType.EndOfFile)) { block = LuigiDataBlock.Inflate(file); bytesRead += block.DeserializeByteCount; if (StopIfScrambleKeyBlockFound(desiredBlockType, block.Type)) { break; } } if (block.Type == desiredBlockType) { dataBlock = block as T; } } } } } return(dataBlock); }
/// <inheritdoc /> public override uint RefreshCrc(out bool changed) { var crc = _crc; if (IsValid) { if (StreamUtilities.FileExists(RomPath)) { uint dontCare; _crc = GetCrcs(RomPath, null, out dontCare); if (crc == 0) { crc = _crc; // lazy initialization means on first read, we should never get a change } } } changed = crc != _crc; return(_crc); }
private static uint GetCrcs(RomFormat format, string romPath, string cfgPath, out uint cfgCrc) { cfgCrc = 0; uint romCrc = 0; if (StreamUtilities.FileExists(romPath)) { byte replacementByte = AutoBaudBytes[format]; #if IGNORE_METADATA_FOR_CRC var metadataRange = new List <Range <int> >(); var metadataOffset = GetMetadataOffset(); metadataRange.Add(new Range <int>(metadataOffset, int.MaxValue)); romCrc = Crc32.OfFile(romPath, format != RomFormat.Intellicart, replacementByte, metadataRange); #else romCrc = Crc32.OfFile(romPath, format != RomFormat.Intellicart, replacementByte); #endif // IGNORE_METADATA_FOR_CRC } return(romCrc); }
/// <summary> /// Gets a path to an existing stock config file path given its canonical CFG identifier. /// </summary> /// <param name="stockConfigFileNumber">A positive integer value indicating which canonical configuration file whose disk location is desired.</param> /// <returns>The absolute path to the file, or <c>null</c> if a file for the given canonical configuration file does not exist.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if <paramref name="stockConfigFileNumber"/> is less than zero.</exception> public static string GetStockCfgFilePath(int stockConfigFileNumber) { var stockCfgFileName = stockConfigFileNumber.ToString(CultureInfo.InvariantCulture) + ProgramFileKind.CfgFile.FileExtension(); var stockCfgUri = new Uri(DefaultToolsDirectory + stockCfgFileName); var stockCfgFilePath = Uri.UnescapeDataString(stockCfgUri.AbsolutePath); // Need to unescape spaces. #if WIN stockCfgFilePath = stockCfgFilePath.Replace('/', System.IO.Path.DirectorySeparatorChar); #elif PCL // NOTE: This will, of course, cause trouble if we build PCL for non-Windows platforms, in which case the proper // solution is to register the 'FixUpUri' method and use it instead... In fact, it would be better to just have a // file system interface to use for stuff like this... Maybe imported via MEF or some such... stockCfgFilePath = stockCfgFilePath.Replace('/', '\\'); #endif // WIN if (!StreamUtilities.FileExists(stockCfgFilePath)) { stockCfgFilePath = null; } return(stockCfgFilePath); }
/// <inheritdoc /> public override uint RefreshCfgCrc(out bool changed) { var cfgCrc = _cfgCrc; if (IsValid) { if (!string.IsNullOrEmpty(ConfigPath)) { if (StreamUtilities.FileExists(ConfigPath)) { GetCrcs(null, ConfigPath, out _cfgCrc); if (cfgCrc == 0) { cfgCrc = _cfgCrc; // lazy initialization means on first read, we should never get a change } } } } changed = cfgCrc != _cfgCrc; return(_cfgCrc); }
/// <summary> /// Ensures that a configuration file can be located for a given ROM. /// </summary> /// <param name="rom">A ROM that may need a configuration (.cfg) file.</param> /// <param name="programInfo">Detailed <see cref="IProgramInformation"/> providing detailed data about the ROM.</param> /// <returns><c>true</c> if <paramref name="rom"/> uses an existing stock CFG file, <c>false</c> otherwise.</returns> /// <remarks>If a ROM requires a configuration file, but one cannot be found, the best possible matching file will be searched for. This is only applicable /// to .bin format ROMs (or their compatriots, .itv and .int files, typically). If <paramref name="rom"/> either does not provide a .cfg file, or /// the file it provides cannot be found, the best possible match based on <paramref name="programInfo"/> will be used. If <paramref name="programInfo"/> /// is also null, the CRC of the ROM will be checked against the active ROM database in memory for a possible stock .cfg file match. If the matching /// stock file is found, then the function returns <c>true</c>.</remarks> public static bool EnsureCfgFileProvided(this IRom rom, IProgramInformation programInfo) { var usesStockCfgFile = false; if ((rom.Format == RomFormat.Bin) && (string.IsNullOrEmpty(rom.ConfigPath) || !StreamUtilities.FileExists(rom.ConfigPath))) { var cfgFilePath = GetStockCfgFile(rom.Crc, rom.RomPath, programInfo); usesStockCfgFile = !string.IsNullOrEmpty(cfgFilePath); if (usesStockCfgFile) { rom.UpdateCfgFile(cfgFilePath); } } return(usesStockCfgFile); }
/// <summary> /// Safely retrieves the LUIGI header for a ROM. /// </summary> /// <param name="rom">The ROM whose LUIGI header is requested.</param> /// <returns>The <see cref="LuigiFileHeader"/> for the ROM, or <c>null</c> if the ROM is not in the LUIGI format.</returns> public static LuigiFileHeader GetLuigiHeader(this IRom rom) { LuigiFileHeader luigiHeader = null; if ((rom != null) && (rom.Format == RomFormat.Luigi) && !string.IsNullOrEmpty(rom.RomPath) && StreamUtilities.FileExists(rom.RomPath) && LuigiFileHeader.PotentialLuigiFile(rom.RomPath)) { luigiHeader = LuigiFileHeader.GetHeader(rom.RomPath); } return(luigiHeader); }
public void StreamUtilitiesWithTestStorage_CallFileExistsWithNonexistentPath_ReturnsFalse() { var storage = new TestStorageAccess(); Assert.False(StreamUtilities.FileExists(@"SomeInvalidPathThatDoesNotExist", storage)); }
/// <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); }