private static string GetRomPathForForkFromCache(Fork fork, string destDir) { string romPath = null; var entry = CacheIndex.Find(fork.Crc24, fork.Size); if (entry != null) { var destFile = System.IO.Path.Combine(destDir, System.IO.Path.GetFileName(entry.RomPath)); if (System.IO.File.Exists(destFile)) { var crc = Crc32.OfFile(destFile); if (crc == entry.RomCrc32) { romPath = destFile; } else { destFile = destFile.EnsureUniqueFileName(); } } if (romPath == null) { var copySourceDir = System.IO.Path.GetDirectoryName(CacheIndex.Path); var sourceFile = System.IO.Path.Combine(copySourceDir, entry.RomPath); System.IO.File.Copy(sourceFile, destFile); romPath = destFile; } } return(romPath); }
private static void SyncDeviceToHost(AsyncTaskData taskData) { var data = (ExecuteDeviceCommandAsyncTaskData)taskData; var device = data.Device; data.Task.UpdateTaskProgress(0, Resources.Strings.DeviceMultistageCommand_UpdatingMenuLayout_ComputingChangesProgress); var customData = (Tuple <MenuLayout, bool>)data.Data; var currentDirtyFlags = GetDirtyFlags.Instance.Execute <LfsDirtyFlags>(device.Port, data); var syncErrors = new FileSystemSyncErrors(currentDirtyFlags); data.Result = syncErrors; // If customData.Item2 is true, that means we should ignore file system inconsistencies, ergo if it's false, we should NOT ignore them. if (currentDirtyFlags.HasFlag(LfsDirtyFlags.FileSystemUpdateInProgress) && !customData.Item2) { throw new InconsistentFileSystemException(Resources.Strings.DeviceMultistageCommand_UpdatingMenuLayout_InconsistentState, device.UniqueId); } var deviceFileSystem = DownloadFileSystemTables.Instance.Execute <FileSystem>(device.Port, data); deviceFileSystem.Status = currentDirtyFlags; if (data.AcceptCancelIfRequested()) { return; } var hostFileSystem = customData.Item1.FileSystem; deviceFileSystem.RemoveMenuPositionData(); // we should not preserve the menu position fork hostFileSystem.PopulateSaveDataForksFromDevice(deviceFileSystem); var allDifferences = deviceFileSystem.CompareTo(hostFileSystem); if (!allDifferences.Any()) { data.Task.CancelTask(); } // We're going to apply the changes to a clone of the original file system. if (data.AcceptCancelIfRequested()) { return; } var fileSystemToModify = hostFileSystem.Clone(); if (data.AcceptCancelIfRequested()) { return; } // Before we change anything, create backups of the ROM list and current menu layout. var configuration = SingleInstanceApplication.Instance.GetConfiguration <Configuration>(); var romsConfiguration = SingleInstanceApplication.Instance.GetConfiguration <INTV.Shared.Model.RomListConfiguration>(); if (System.IO.File.Exists(configuration.MenuLayoutPath) || System.IO.File.Exists(romsConfiguration.RomFilesPath)) { var backupTimestamp = INTV.Shared.Utility.PathUtils.GetTimeString(); var backupSubdirectory = configuration.SyncFromDeviceBackupFilenameFragment + "-" + backupTimestamp; var backupDirectory = System.IO.Path.Combine(configuration.HostBackupDataAreaPath, backupSubdirectory); if (!System.IO.Directory.Exists(backupDirectory)) { System.IO.Directory.CreateDirectory(backupDirectory); } if (System.IO.File.Exists(configuration.MenuLayoutPath)) { var backupMenuLayoutPath = System.IO.Path.Combine(backupDirectory, configuration.DefaultMenuLayoutFileName); System.IO.File.Copy(configuration.MenuLayoutPath, backupMenuLayoutPath); } if (System.IO.File.Exists(romsConfiguration.RomFilesPath)) { var backupRomListPath = System.IO.Path.Combine(backupDirectory, romsConfiguration.DefaultRomsFileName); System.IO.File.Copy(romsConfiguration.RomFilesPath, backupRomListPath); } } // First, directory deletion. if (!data.CancelRequsted) { foreach (var directory in allDifferences.DirectoryDifferences.ToDelete) { if (data.AcceptCancelIfRequested()) { break; } // We don't use RemoveAt because of the collateral damage it may cause. fileSystemToModify.Directories[(int)directory] = null; } } // Then, we process file deletion. if (!data.CancelRequsted) { foreach (var file in allDifferences.FileDifferences.ToDelete) { if (data.AcceptCancelIfRequested()) { break; } // We don't use RemoveAt because of the collateral damage it may cause. fileSystemToModify.Files[(int)file] = null; } } // Finally, fork deletion. if (!data.CancelRequsted) { foreach (var fork in allDifferences.ForkDifferences.ToDelete) { if (data.AcceptCancelIfRequested()) { break; } // We don't use RemoveAt because of the collateral damage it may cause. fileSystemToModify.Forks[(int)fork] = null; } } var roms = SingleInstanceApplication.Instance.Roms; var romsToAdd = new HashSet <string>(); var forkNumberMap = new Dictionary <ushort, ushort>(); // maps device -> local var forkSourceFileMap = new Dictionary <ushort, string>(); // maps local fork number -> source file for fork // Add new forks. if (!data.CancelRequsted) { foreach (var fork in allDifferences.ForkDifferences.ToAdd) { if (data.AcceptCancelIfRequested()) { break; } // Forks on device but not in our menu must be added. System.Diagnostics.Debug.WriteLine("Fork adds! This means CRC24 of a LUIGI -> ???"); Fork newLocalFork = null; var forkSourcePath = GetSourcePathForFork(data, fork, deviceFileSystem, roms, romsConfiguration); // retrieves the fork if necessary if (forkSourcePath != null) { newLocalFork = new Fork(forkSourcePath) { GlobalForkNumber = fork.GlobalForkNumber }; romsToAdd.Add(forkSourcePath); } else { // Is this code even reachable any more??? Looking at GetSourcePathForFork, I would think not. newLocalFork = new Fork(fork.Crc24, fork.Size, fork.GlobalForkNumber); var cacheEntry = CacheIndex.Find(fork.Crc24, fork.Size); if (cacheEntry != null) { newLocalFork.FilePath = System.IO.Path.Combine(configuration.RomsStagingAreaPath, cacheEntry.LuigiPath); } } newLocalFork.FileSystem = fileSystemToModify; fileSystemToModify.Forks.AddAndRelocate(newLocalFork); forkNumberMap[fork.GlobalForkNumber] = newLocalFork.GlobalForkNumber; if (string.IsNullOrEmpty(forkSourcePath)) { System.Diagnostics.Debug.WriteLine("Bad path in Fork add."); syncErrors.UnableToRetrieveForks.Add(newLocalFork); } forkSourceFileMap[newLocalFork.GlobalForkNumber] = forkSourcePath; } } // Update forks. if (!data.CancelRequsted) { foreach (var fork in allDifferences.ForkDifferences.ToUpdate) { if (data.AcceptCancelIfRequested()) { break; } // Forks on the device don't store file paths. var localFork = fileSystemToModify.Forks[fork.GlobalForkNumber]; forkNumberMap[fork.GlobalForkNumber] = localFork.GlobalForkNumber; var forkSourcePath = GetSourcePathForFork(data, fork, deviceFileSystem, roms, romsConfiguration); if (string.IsNullOrEmpty(forkSourcePath)) { System.Diagnostics.Debug.WriteLine("Bad path in Fork update."); syncErrors.UnableToRetrieveForks.Add(localFork); } forkSourceFileMap[localFork.GlobalForkNumber] = forkSourcePath; localFork.FilePath = forkSourcePath; if ((localFork.Crc24 != fork.Crc24) || (localFork.Size != fork.Size)) { localFork.FilePath = null; // May need to regenerate the LUIGI file... System.Diagnostics.Debug.WriteLine("Fork at path doesn't match! " + forkSourcePath); syncErrors.UnableToRetrieveForks.Add(localFork); } ProgramDescription description = null; var forkKind = deviceFileSystem.GetForkKind(fork); if (SyncForkData(localFork, forkKind, forkSourceFileMap, null, ref description)) { IEnumerable <ILfsFileInfo> filesUsingFork; if (fileSystemToModify.GetAllFilesUsingForks(new[] { localFork }).TryGetValue(localFork, out filesUsingFork)) { foreach (var program in filesUsingFork.OfType <Program>()) { // This situation can arise when we have a ROM on the local system that has the // same CRC as the one in the device's file system, but whose .cfg file has drifted // from what was in place when the LUIGI file on the device was initially created // and deployed. What we need to do in this case, then, is to force the programs // pointing to this fork to actually use the LUIGI file now. // The LUIGI file has already been put into the right place -- it's now a matter of // forcing the program to actually point to it. This runs a bit counter to how several // IRom implementations are wrappers around other types of ROMs. romsToAdd.Add(forkSourcePath); } } } else { syncErrors.UnableToRetrieveForks.Add(localFork); } } } var recoveredRomFiles = romsToAdd.IdentifyRomFiles(data.AcceptCancelIfRequested, (f) => data.UpdateTaskProgress(0, f)); var recoveredRoms = ProgramCollection.GatherRomsFromFileList(recoveredRomFiles, roms, null, data.AcceptCancelIfRequested, (f) => data.UpdateTaskProgress(0, f), null, null); if (roms.AddNewItemsFromList(recoveredRoms).Any()) { roms.Save(romsConfiguration.RomFilesPath, false); // this may throw an error, which, in this case, will terminate the operation } // Add files. if (!data.CancelRequsted) { foreach (var file in allDifferences.FileDifferences.ToAdd) { if (data.AcceptCancelIfRequested()) { break; } var fileNode = FileNode.Create((LfsFileInfo)file); fileNode.FileSystem = fileSystemToModify; fileSystemToModify.Files.AddAndRelocate(fileNode); if (file.FileType == FileType.Folder) { fileSystemToModify.Directories.AddAndRelocate((IDirectory)fileNode); } SyncFileData(fileNode, file, false, forkNumberMap, forkSourceFileMap, syncErrors); } } // Update files. var fixups = new Dictionary <FileNode, ILfsFileInfo>(); if (!data.CancelRequsted) { foreach (var file in allDifferences.FileDifferences.ToUpdate) { if (data.AcceptCancelIfRequested()) { break; } var localFile = (FileNode)fileSystemToModify.Files[file.GlobalFileNumber]; if (localFile.FileType != file.FileType) { // We've got a case of a file changing to / from a directory, which must be handled differently. // Simply null out the entry to prevent unwanted collateral damage, such as deleted forks or other files. fileSystemToModify.Files[localFile.GlobalFileNumber] = null; var localParent = (Folder)localFile.Parent; var localGdn = localFile.GlobalDirectoryNumber; var indexInParent = localParent.IndexOfChild(localFile); localFile = FileNode.Create((LfsFileInfo)file); localFile.FileSystem = fileSystemToModify; localFile.GlobalFileNumber = file.GlobalFileNumber; fileSystemToModify.Files.Add(localFile); SyncFileData(localFile, file, true, forkNumberMap, forkSourceFileMap, syncErrors); switch (file.FileType) { case FileType.File: ////System.Diagnostics.Debug.Assert(localFile.FileType == file.FileType, "File type mutation! Need to implement!"); // The directory on the local file system is being replaced with a file. It's possible that the directory // itself has been reparented to a new location, so we do not 'destructively' remove it. Instead, we will // null out its entry in the GDT / GFT and replace the GFT entry with a new file. fileSystemToModify.Directories[localGdn] = null; // so we don't accidentally nuke files and their forks - directories will be updated later break; case FileType.Folder: // The file on the local file system is a standard file, but on the device, it's now a directory. // Need to remove the file and create a directory in its place. We'll also need to populate the directory. // The directory population will need to happen after we've completely finished all the adds / updates. localFile.GlobalDirectoryNumber = file.GlobalDirectoryNumber; fileSystemToModify.Directories.Add((Folder)localFile); break; } localParent.Files[indexInParent] = localFile; fixups[localFile] = file; } else { SyncFileData(localFile, file, true, forkNumberMap, forkSourceFileMap, syncErrors); } } } // Add directories. if (!data.CancelRequsted) { foreach (var directory in allDifferences.DirectoryDifferences.ToAdd) { if (data.AcceptCancelIfRequested()) { break; } // Directory itself may already be in the file system. Now, we need to set contents correctly. var localFolder = (Folder)fileSystemToModify.Directories[directory.GlobalDirectoryNumber]; if (localFolder == null) { System.Diagnostics.Debug.WriteLine("Where's my dir?"); syncErrors.FailedToCreateEntries.Add(directory); } for (var i = 0; i < directory.PresentationOrder.ValidEntryCount; ++i) { if (data.AcceptCancelIfRequested()) { break; } var childToAdd = fileSystemToModify.Files[directory.PresentationOrder[i]]; localFolder.AddChild((IFile)childToAdd, false); } } } // Update directories. if (!data.CancelRequsted) { foreach (var directory in allDifferences.DirectoryDifferences.ToUpdate) { // Need to keep contents in sync. All other changes were handled by file update. var localFolder = (Folder)fileSystemToModify.Directories[directory.GlobalDirectoryNumber]; if (localFolder == null) { localFolder = new Folder(fileSystemToModify, directory.GlobalDirectoryNumber, string.Empty); } var localNumEntries = localFolder.PresentationOrder.ValidEntryCount; var devicePresentationOrder = directory.PresentationOrder; for (var i = devicePresentationOrder.ValidEntryCount; !data.CancelRequsted && (i < localNumEntries); ++i) { if (data.AcceptCancelIfRequested()) { break; } var prevFile = localFolder.Files[devicePresentationOrder.ValidEntryCount]; var prevParent = prevFile.Parent; localFolder.Files.RemoveAt(devicePresentationOrder.ValidEntryCount); prevFile.Parent = prevParent; } localNumEntries = localFolder.PresentationOrder.ValidEntryCount; for (var i = 0; !data.CancelRequsted && (i < (devicePresentationOrder.ValidEntryCount - localNumEntries)); ++i) { if (data.AcceptCancelIfRequested()) { break; } localFolder.Files.Add((FileNode)fileSystemToModify.Files[devicePresentationOrder[i]]); } System.Diagnostics.Debug.Assert(localFolder.Files.Count == devicePresentationOrder.ValidEntryCount, "Incorrect number of children in directory!"); for (var i = 0; !data.CancelRequsted && (i < devicePresentationOrder.ValidEntryCount); ++i) { if (data.AcceptCancelIfRequested()) { break; } var localFile = (FileNode)fileSystemToModify.Files[devicePresentationOrder[i]]; if (!ReferenceEquals(localFolder.Files[i], localFile)) { var prevFile = localFolder.Files[i]; var prevFileParent = prevFile == null ? null : prevFile.Parent; localFolder.Files[i] = localFile; localFile.Parent = localFolder; if (prevFile != null) { // The default behavior of item replacement is to null the parent of the existing item. // Therefore, because some item rearrangement results in a file shuffling 'up' or 'down' // in the same folder, we need to retain the parent. So if the item is still in here, // reset its parent. prevFile.Parent = prevFileParent; } } } } } // Now, pass back the new MenuLayout so UI thread can update and save. if (!data.CancelRequsted && data.Succeeded) { syncErrors.Data = (MenuLayout)fileSystemToModify.Directories[GlobalDirectoryTable.RootDirectoryNumber]; } }
private static bool SyncForkData(Fork fork, ForkKind forkKind, Dictionary <ushort, string> forkSourceFileMap, Action <Fork, ForkKind> errorAction, ref ProgramDescription description) { var succeeded = true; if (fork != null) { var crc = 0u; switch (forkKind) { case ForkKind.Program: var cfgCrc = 0u; var cacheEntry = CacheIndex.Find(fork.Crc24, fork.Size); if (cacheEntry != null) { crc = cacheEntry.RomCrc32; cfgCrc = cacheEntry.CfgCrc32; } else { string romPath; if (forkSourceFileMap.TryGetValue(fork.GlobalForkNumber, out romPath) && !string.IsNullOrEmpty(romPath) && System.IO.File.Exists(romPath)) { if (LuigiFileHeader.PotentialLuigiFile(romPath)) { var luigiHeader = LuigiFileHeader.GetHeader(romPath); if (luigiHeader.Version > 0) { crc = luigiHeader.OriginalRomCrc32; cfgCrc = luigiHeader.OriginalCfgCrc32; } } if (crc == 0u) { crc = Crc32.OfFile(romPath); } } else { System.Console.WriteLine("SDFS"); } } if (crc != 0) { var romListDescriptions = INTV.Shared.Model.Program.ProgramCollection.Roms.Where(d => d.Crc == crc); if (romListDescriptions.Any()) { var romListDescription = romListDescriptions.FirstOrDefault(d => (d.Rom != null) && (d.Rom.CfgCrc == cfgCrc)); if (romListDescription != null) { description = romListDescription.Copy(); } } } if (description == null) { var rom = fork.Rom; if (rom == null) { string romPath; succeeded = forkSourceFileMap.TryGetValue(fork.GlobalForkNumber, out romPath); if (succeeded) { rom = Rom.Create(romPath, null); fork.Rom = rom; if (string.IsNullOrEmpty(fork.FilePath)) { fork.FilePath = romPath; } } } if (rom != null) { var programInfo = rom.GetProgramInformation(); description = new ProgramDescription(rom.Crc, rom, programInfo); } } break; case ForkKind.Manual: string filePath; if (forkSourceFileMap.TryGetValue(fork.GlobalForkNumber, out filePath) && System.IO.File.Exists(filePath)) { fork.FilePath = filePath; if (description != null) { description.Files.DefaultManualTextPath = fork.FilePath; } } break; case ForkKind.JlpFlash: // TODO / FIXME : We don't do anything with JLP save data forks when syncing from the file system. break; case ForkKind.Vignette: case ForkKind.Reserved4: case ForkKind.Reserved5: case ForkKind.Reserved6: succeeded = false; break; } } if (!succeeded && (errorAction != null)) { errorAction(fork, forkKind); } return(succeeded); }
/// <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 }