private void AddEntry(CacheIndexEntry entry, bool save) { var anyMatching = Entries.Any(e => (e.LuigiCrc24 == entry.LuigiCrc24) && (e.LuigiSize == entry.LuigiSize) && (e.RomCrc32 == entry.RomCrc32) && (e.RomSize == entry.RomSize)); if (!anyMatching) { Entries.Add(entry); if (save) { Save(); } } }
/// <summary> /// Gathers cache data to rebuild it if the index file is missing. /// </summary> /// <param name="cacheIndexDirectory">The director for which to rebuild the index.</param> /// <param name="rebuildIndexErrorMessages">Accumulates any errors encountered during the rebuild..</param> /// <returns>An enumerable containing index entries from the rebuild process.</returns> public static IEnumerable <CacheIndexEntry> GatherCacheData(string cacheIndexDirectory, IList <string> rebuildIndexErrorMessages) { var jzIntvConfiguration = SingleInstanceApplication.Instance.GetConfiguration <INTV.JzIntv.Model.Configuration>(); var cachedRoms = (new[] { cacheIndexDirectory }).IdentifyRomFiles(() => false, f => { }).ToList(); var cachedLuigiRoms = cachedRoms.Where(r => r.Format == RomFormat.Luigi).ToList(); var nonLuigiRoms = cachedRoms.Except(cachedLuigiRoms).ToList(); var restoredCacheEntries = new List <CacheIndexEntry>(); var cacheDirectoryLength = cacheIndexDirectory.Length + 1; // for cutting backslash out later... using (var comparer = CanonicalRomComparerStrict.Default) { foreach (var cachedLuigiRom in cachedLuigiRoms) { // Get the LUIGI header. LuigiFileHeader luigiHeader = LuigiFileHeader.GetHeader(cachedLuigiRom.RomPath); // Initialize the LUIGI part of the cache entry. var cacheEntry = new CacheIndexEntry(); cacheEntry.LuigiPath = cachedLuigiRom.RomPath.Substring(cacheDirectoryLength); cacheEntry.LuigiCrc24 = INTV.Core.Utility.Crc24.OfFile(cachedLuigiRom.RomPath); cacheEntry.LuigiSize = (uint)(new FileInfo(cachedLuigiRom.RomPath)).Length; // Now fetch or recreate the ROM if missing. RomFormat.None indicates that the // original has been located on disk. Otherwise, indicates the format to recreate from the LUIGI file. var originalRomFormat = RomFormat.None; if (luigiHeader.Version > 0) { // LUIGI format 1 and later contains information about the original ROM. cacheEntry.RomCrc32 = luigiHeader.OriginalRomCrc32; var originalRom = nonLuigiRoms.FirstOrDefault(r => (r.Format == luigiHeader.OriginalRomFormat) && r.IsEquivalentTo(cachedLuigiRom, comparer)); if (originalRom != null) { cacheEntry.RomPath = originalRom.RomPath.Substring(cacheDirectoryLength); cacheEntry.RomSize = (uint)(new FileInfo(originalRom.RomPath)).Length; restoredCacheEntries.Add(cacheEntry); nonLuigiRoms.RemoveAll(r => (r.Format == luigiHeader.OriginalRomFormat) && r.IsEquivalentTo(cachedLuigiRom, comparer)); } else { // Write down the format to reconstruct. originalRomFormat = luigiHeader.OriginalRomFormat; } } else { // Check for a .bin or .rom file. originalRomFormat = RomFormat.Rom; var originalFile = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, originalRomFormat.FileExtension()); if (!File.Exists(originalFile)) { originalRomFormat = RomFormat.Bin; foreach (var binFormatExtension in ProgramFileKindHelpers.RomFileExtensionsThatUseCfgFiles) { if (!string.IsNullOrEmpty(binFormatExtension)) { originalFile = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, binFormatExtension); } else { originalFile = System.IO.Path.GetFileNameWithoutExtension(cachedLuigiRom.RomPath); } if (File.Exists(originalFile)) { break; } } } if (File.Exists(originalFile)) { cacheEntry.RomCrc32 = INTV.Core.Utility.Crc32.OfFile(originalFile); cacheEntry.RomPath = originalFile.Substring(cacheDirectoryLength); cacheEntry.RomSize = (uint)(new FileInfo(originalFile)).Length; if (originalRomFormat == RomFormat.Bin) { var cfgPath = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, ProgramFileKind.CfgFile.FileExtension()); if (!File.Exists(cfgPath)) { cacheEntry.RestoreCfgFile(); } } restoredCacheEntries.Add(cacheEntry); nonLuigiRoms.RemoveAll(r => (r.Format == originalRomFormat) && r.IsEquivalentTo(cachedLuigiRom, comparer)); originalRomFormat = RomFormat.None; } } if (originalRomFormat != RomFormat.None) { // Need to recreate the ROM from LUIGI. var sourcePath = "\"" + System.IO.Path.GetFileNameWithoutExtension(cachedLuigiRom.RomPath) + "\""; var workingDir = System.IO.Path.GetDirectoryName(cachedLuigiRom.RomPath); var conversionApps = jzIntvConfiguration.GetConverterApps(cachedLuigiRom, luigiHeader.OriginalRomFormat); var conversionResult = 0; foreach (var conversionApp in conversionApps) { var argument = luigiHeader.OriginalRomFormat.GetCommandLineArgForBin2Rom() + sourcePath; conversionResult = INTV.Shared.Utility.RunExternalProgram.Call(conversionApp.Item1, argument, workingDir); if (conversionResult != 0) { rebuildIndexErrorMessages.Add("Failed to reconstruct " + sourcePath + luigiHeader.OriginalRomFormat.FileExtension()); break; } } if (conversionResult == 0) { cacheEntry.RomPath = System.IO.Path.ChangeExtension(cacheEntry.LuigiPath, luigiHeader.OriginalRomFormat.FileExtension()); cacheEntry.RomSize = (uint)(new FileInfo(System.IO.Path.Combine(cacheIndexDirectory, cacheEntry.RomPath))).Length; if (originalRomFormat == RomFormat.Bin) { var cfgPath = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, ProgramFileKind.CfgFile.FileExtension()); if (!File.Exists(cfgPath)) { cacheEntry.RestoreCfgFile(); } #if false if (File.Exists(cfgPath)) { cacheEntry.CfgCrc32 = INTV.Core.Utility.Crc32.OfFile(cfgPath); cacheEntry.CfgPath = cfgPath.Substring(cacheDirectoryLength); cacheEntry.CfgSize = (uint)(new FileInfo(cfgPath)).Length; } #endif // false } restoredCacheEntries.Add(cacheEntry); } } } foreach (var nonLuigiRom in nonLuigiRoms) { var cacheEntry = new CacheIndexEntry(); cacheEntry.RomPath = nonLuigiRom.RomPath.Substring(cacheDirectoryLength); cacheEntry.RomCrc32 = nonLuigiRom.Crc; cacheEntry.RomSize = (uint)(new FileInfo(nonLuigiRom.RomPath)).Length; if (nonLuigiRom.Format == RomFormat.Bin) { var cfgPath = System.IO.Path.ChangeExtension(nonLuigiRom.RomPath, ProgramFileKind.CfgFile.FileExtension()); if (!File.Exists(cfgPath)) { cacheEntry.RestoreCfgFile(); #if false var programInfo = ProgramInformationTable.Default.FindProgram(cacheEntry.RomCrc32); var cfgFilePath = nonLuigiRom.GenerateStockCfgFile(programInfo); if (string.Compare(cfgFilePath, cfgPath, true) != 0) { System.Diagnostics.Debug.WriteLine("LSDKFLSKDFJLSJDF"); } #endif // false } #if false if (File.Exists(cfgPath)) { cacheEntry.CfgCrc32 = INTV.Core.Utility.Crc32.OfFile(cfgPath); cacheEntry.CfgPath = cfgPath.Substring(cacheDirectoryLength); cacheEntry.CfgSize = (uint)(new FileInfo(cfgPath)).Length; } #endif // false } var sourcePath = System.IO.Path.GetFileNameWithoutExtension(nonLuigiRom.RomPath); var workingDir = System.IO.Path.GetDirectoryName(nonLuigiRom.RomPath); var conversionApp = jzIntvConfiguration.GetConverterApps(nonLuigiRom, RomFormat.Luigi).First(); var result = INTV.Shared.Utility.RunExternalProgram.Call(conversionApp.Item1, "\"" + sourcePath + "\"", workingDir); if (result != 0) { rebuildIndexErrorMessages.Add(string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RestoreCacheIndex_FailedToReconstructFormat, sourcePath + RomFormat.Luigi.FileExtension())); } else { cacheEntry.LuigiPath = System.IO.Path.ChangeExtension(cacheEntry.RomPath, RomFormat.Luigi.FileExtension()); var fullLuigiPath = System.IO.Path.Combine(cacheIndexDirectory, cacheEntry.LuigiPath); cacheEntry.LuigiCrc24 = INTV.Core.Utility.Crc24.OfFile(fullLuigiPath); cacheEntry.LuigiSize = (uint)(new FileInfo(fullLuigiPath)).Length; restoredCacheEntries.Add(cacheEntry); } } } return(restoredCacheEntries); }
/// <summary> /// Adds an entry to the index if appropriate. /// </summary> /// <param name="entry">The entry to add.</param> public void AddEntry(CacheIndexEntry entry) { AddEntry(entry, true); }
/// <summary> /// Prepares a ROM for deployment to a Locutus device. /// </summary> /// <param name="rom">The ROM being prepared.</param> /// <param name="updateMode">Specifies the behavior of the LUIGI file generation.</param> /// <returns>The fully qualified path of the prepared output data file to deploy to Locutus.</returns> public static string PrepareForDeployment(this IRom rom, LuigiGenerationMode updateMode) { #if REPORT_PERFORMANCE #if RECORD_PREPARE_FOR_DEPLOYMENT_VISITS int visits; if (PrepareForDeploymentVisits.TryGetValue(rom.RomPath, out visits)) { PrepareForDeploymentVisits[rom.RomPath] = ++visits; } else { PrepareForDeploymentVisits[rom.RomPath] = 1; } #endif // RECORD_PREPARE_FOR_DEPLOYMENT_VISITS var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { var stopwatch2 = System.Diagnostics.Stopwatch.StartNew(); #endif // REPORT_PERFORMANCE rom.Validate(); #if REPORT_PERFORMANCE stopwatch2.Stop(); _accumulatedPrepareValidateTime += stopwatch2.Elapsed; #endif // REPORT_PERFORMANCE if ((updateMode == LuigiGenerationMode.Passthrough) && (rom.Format == RomFormat.Luigi)) { return(rom.RomPath); } #if REPORT_PERFORMANCE stopwatch2.Restart(); var stopwatch3 = System.Diagnostics.Stopwatch.StartNew(); #endif // REPORT_PERFORMANCE var jzIntvConfiguration = SingleInstanceApplication.Instance.GetConfiguration <INTV.JzIntv.Model.Configuration>(); var converterApps = jzIntvConfiguration.GetConverterApps(rom, RomFormat.Luigi); if (!converterApps.Any()) { converterApps = new[] { new Tuple <string, RomFormat>(JustCopy, RomFormat.Luigi) }; } var converterApp = converterApps.First(); // rom.GetConverterApp(jzIntvConfiguration); if ((converterApp.Item1 != JustCopy) && (string.IsNullOrEmpty(converterApp.Item1) || !System.IO.File.Exists(converterApp.Item1)) && (rom.Format != RomFormat.Luigi)) { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_ConversionToolNotFound_Error_Format, converterApp); throw new LuigiFileGenerationException(message, Resources.Strings.RomToLuigiFailed_ConversionToolNotFound_Error_Description); } #if REPORT_PERFORMANCE stopwatch3.Stop(); _accumulatedPrepareConverterAppsTime += stopwatch3.Elapsed; stopwatch3.Restart(); #endif // REPORT_PERFORMANCE var romStagingArea = SingleInstanceApplication.Instance.GetConfiguration <Configuration>().RomsStagingAreaPath; var stagingAreaPath = rom.GetStagingAreaPath(romStagingArea); var cachedRomPath = rom.GetCachedRomFilePath(stagingAreaPath); var cachedConfigPath = rom.GetCachedConfigFilePath(stagingAreaPath); var luigiFile = rom.GetOutputFilePath(stagingAreaPath, ProgramFileKind.LuigiFile); #if REPORT_PERFORMANCE stopwatch3.Stop(); _accumulatedPrepareStagingTime += stopwatch3.Elapsed; stopwatch3.Restart(); #endif // REPORT_PERFORMANCE bool createLuigiFile = true; bool changed; bool isSourceFileInCache = rom.IsInCache(stagingAreaPath, out changed); #if REPORT_PERFORMANCE stopwatch3.Stop(); _accumulatedPrepareCacheLookupTime += stopwatch3.Elapsed; #endif // REPORT_PERFORMANCE #if REPORT_PERFORMANCE stopwatch2.Stop(); _accumulatedPrepareSetupTime += stopwatch2.Elapsed; stopwatch2.Restart(); #endif // REPORT_PERFORMANCE var luigiHeader = rom.GetLuigiHeader(); if (luigiHeader != null) { // If the given ROM is already a LUIGI file, see if we can determine whether it's already in our cache. #if REPORT_OLD_LUIGI_FILES System.Diagnostics.Debug.Assert(luigiHeader.Version > 0, "Really, you've got some OLD LUIGI files. Delete them."); #endif // REPORT_OLD_LUIGI_FILES var crc24 = INTV.Core.Utility.Crc24.OfFile(rom.RomPath); var size = new System.IO.FileInfo(rom.RomPath).Length; var entry = CacheIndex.Find(crc24, (uint)size); isSourceFileInCache = entry != null; if (isSourceFileInCache) { // Cases have been found in which, by moving files around on disk, the staging area path can change. // The result of this, though, is that the *new* path in the cache is different than the extant one // found in the cache. In this case, if the entry's location is different than the newly computed // one, ignore the cache entry and make a new one by acting as if the file is not in the cache. // FIXME This is a lazy fix. A better fix would be to remove the cached files and existing cache // entry and re-add this new one. Or patch up the existing entry. Hell, maybe scrap the entire cache // altogether as it's a bit of a bug farm and creating LUIGI files isn't all *that* expensive. var stagingDirectory = System.IO.Path.GetFileName(stagingAreaPath); var stagingPathChanged = !entry.LuigiPath.StartsWith(stagingDirectory); if (stagingPathChanged) { isSourceFileInCache = false; } else { luigiFile = System.IO.Path.Combine(romStagingArea, entry.LuigiPath); cachedRomPath = System.IO.Path.Combine(romStagingArea, entry.RomPath); if (!string.IsNullOrEmpty(entry.CfgPath)) { cachedConfigPath = System.IO.Path.Combine(romStagingArea, entry.CfgPath); } } } } #if REPORT_PERFORMANCE stopwatch2.Stop(); _accumulatedPrepareLuigiHeaderTime += stopwatch2.Elapsed; stopwatch2.Restart(); #endif // REPORT_PERFORMANCE if (isSourceFileInCache) { createLuigiFile = changed || !System.IO.File.Exists(luigiFile); } if (!isSourceFileInCache || changed) { cachedRomPath.ClearReadOnlyAttribute(); cachedConfigPath.ClearReadOnlyAttribute(); System.IO.File.Copy(rom.RomPath, cachedRomPath, true); if (!string.IsNullOrWhiteSpace(cachedConfigPath) && !string.IsNullOrEmpty(rom.ConfigPath) && System.IO.File.Exists(rom.ConfigPath) && (rom.ConfigPath != rom.RomPath)) { System.IO.File.Copy(rom.ConfigPath, cachedConfigPath, true); } else if ((string.IsNullOrEmpty(rom.ConfigPath) || !System.IO.File.Exists(rom.ConfigPath)) && System.IO.File.Exists(cachedConfigPath)) { // The ROM's configuration file path doesn't exist, but there's one in the cache. Remove it. FileUtilities.DeleteFile(cachedConfigPath, false, 2); cachedConfigPath = null; // this is OK, because the ClearReadOnlyAttribute() extension method is null-safe } cachedRomPath.ClearReadOnlyAttribute(); cachedConfigPath.ClearReadOnlyAttribute(); } #if REPORT_PERFORMANCE stopwatch2.Stop(); _accumulatedPrepareCachedChangedTime += stopwatch2.Elapsed; stopwatch2.Restart(); #endif // REPORT_PERFORMANCE if (createLuigiFile || ((updateMode == LuigiGenerationMode.FeatureUpdate) || (updateMode == LuigiGenerationMode.Reset))) { var argument = "\"" + cachedRomPath + "\"" + " \"" + luigiFile + "\""; var result = -1; if (JustCopy == converterApp.Item1) { System.IO.File.Copy(rom.RomPath, luigiFile, true); result = 0; } else { result = INTV.Shared.Utility.RunExternalProgram.Call(converterApp.Item1, argument, stagingAreaPath); } if (result == 0) { if (!System.IO.File.Exists(luigiFile)) { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_OutputFileNotFound_Error_Format, rom.RomPath, System.IO.Path.GetFileNameWithoutExtension(luigiFile)); throw new LuigiFileGenerationException(message, Resources.Strings.RomToLuigiFailed_OutputFileNotFound_Error_Description_Format); } else if ((new System.IO.FileInfo(luigiFile)).Length > Device.TotalRAMSize) { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_TooLarge_Error_Message_Format, rom.RomPath, luigiFile); throw new LuigiFileGenerationException(message, Resources.Strings.RomToLuigiFailed_TooLarge_Description); } var description = INTV.Shared.Model.Program.ProgramCollection.Roms.FirstOrDefault(d => rom.IsEquivalentTo(d.Rom, RomComparerStrict.Default)); if (description != null) { LuigiFeatureFlags features = LuigiFeatureFlags.None; #if DEBUG var complainAboutOldLuigiFile = false; #endif // DEBUG luigiHeader = LuigiFileHeader.GetHeader(luigiFile); features = description.Features.LuigiFeaturesLo; #if DEBUG var isRecognizedRom = !description.Features.GeneralFeatures.HasFlag(GeneralFeatures.UnrecognizedRom); var hasFlagsFromCfgFile = luigiHeader.Features.HasFlag(LuigiFeatureFlags.FeatureFlagsExplicitlySet); complainAboutOldLuigiFile = isRecognizedRom && hasFlagsFromCfgFile && ((luigiHeader.Features & ~LuigiFeatureFlags.FeatureFlagsExplicitlySet) != features); #endif // DEBUG #if DEBUG if (complainAboutOldLuigiFile) { var message = "Known ROM has explicit flags from utility that are different than those set by LUI:\n\n"; message += " LUI: " + features + "\n"; message += " Utility: " + (luigiHeader.Features & ~LuigiFeatureFlags.FeatureFlagsExplicitlySet); INTV.Shared.View.OSMessageBox.Show(message, "Feature Flags Inconsistency"); } #endif // DEBUG if (luigiHeader.WouldModifyFeatures(features, updateMode == LuigiGenerationMode.FeatureUpdate)) { using (var file = System.IO.File.Open(luigiFile, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite)) { luigiHeader.UpdateFeatures(features, updateMode == LuigiGenerationMode.FeatureUpdate); file.Seek(0, System.IO.SeekOrigin.Begin); luigiHeader.Serialize(new Core.Utility.BinaryWriter(file)); } } } try { var cacheIndexEntry = new CacheIndexEntry(rom, cachedRomPath); CacheIndex.Instance.AddEntry(cacheIndexEntry); } catch (Exception e) { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.PrepareForDeployment_ErrorCreatingCacheEntryFormat, rom.RomPath); throw new LuigiFileGenerationException(message, e.Message, e); } } else { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_InvalidOperation_Error_Message_Format, result); var description = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_Error_Description_Format, converterApp); throw new LuigiFileGenerationException(message, description); } } else { // If this is a different ROM that produces the same LUIGI, add an entry. var crc24 = INTV.Core.Utility.Crc24.OfFile(luigiFile); var size = (uint)(new System.IO.FileInfo(luigiFile)).Length; if (CacheIndex.Find(crc24, size) == null) { try { var cacheIndexEntry = new CacheIndexEntry(rom, cachedRomPath); CacheIndex.Instance.AddEntry(cacheIndexEntry); } catch (Exception e) { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.PrepareForDeployment_ErrorCreatingCacheEntryFormat, rom.RomPath); throw new LuigiFileGenerationException(message, e.Message, e); } } } ////catch (System.IO.PathTooLongException e) ////catch (System.IO.IOException e) ////catch (UnauthorizedAccessException e) ////catch (InvalidOperationException e) ////catch (LuigiFileGenerationException e) #if REPORT_PERFORMANCE stopwatch2.Stop(); _accumulatedPrepareLuigiUpdateTime += stopwatch2.Elapsed; #endif // REPORT_PERFORMANCE if (string.IsNullOrEmpty(luigiFile) || !System.IO.File.Exists(luigiFile)) { var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_Error_Description_Format, rom); var description = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_InvalidOutputFileFormat, luigiFile); throw new LuigiFileGenerationException(message, description); } #if REPORT_PERFORMANCE stopwatch.Stop(); #endif // REPORT_PERFORMANCE return(luigiFile); #if REPORT_PERFORMANCE } finally { stopwatch.Stop(); _accumulatedPrepareTime += stopwatch.Elapsed; } #endif // REPORT_PERFORMANCE }