private static void SetFileSystemFlags(AsyncTaskData taskData) { var data = (ExecuteDeviceCommandAsyncTaskData)taskData; var device = data.Device; data.Succeeded = SetDirtyFlags.Create((LfsDirtyFlags)data.Data).Execute <bool>(device.Port, data); }
/// <summary> /// If the operation should update the file system's dirty flags, and hasn't done so already, do so. /// </summary> /// <param name="currentDirtyFlags">The current dirty flags on the file system.</param> /// <param name="fileSystem">The file system being updated.</param> /// <param name="taskData">Task data for displaying updates, tracking success, etc.</param> /// <param name="globalFileSystemNumber">If an error occurs, the entity type and its global file system number value are used to report the error.</param> /// <param name="operation">The kind of file system operation being done.</param> /// <param name="targetType">The kind of file system entity involved in the operation.</param> /// <returns>The new dirty flags.</returns> private static LfsDirtyFlags UpdateFileSystemDirtyState(this LfsDirtyFlags currentDirtyFlags, FileSystem fileSystem, ExecuteDeviceCommandAsyncTaskData taskData, uint globalFileSystemNumber, LfsOperations operation, LfsEntityType targetType) { if (taskData.Succeeded && !currentDirtyFlags.HasFlag(LfsDirtyFlags.FileSystemUpdateInProgress)) { currentDirtyFlags |= LfsDirtyFlags.FileSystemUpdateInProgress; taskData.Succeeded = SetDirtyFlags.Create(currentDirtyFlags).Execute <bool>(taskData.Device.Port, taskData); } if (!taskData.Succeeded && currentDirtyFlags.HasFlag(LfsDirtyFlags.FileSystemUpdateInProgress)) { var errorFormatString = Resources.Strings.FileSystem_InconsistencyError_Format; if (operation == LfsOperations.Remove) { errorFormatString = Resources.Strings.FileSystem_InconsistencyError_Deleting_Format; } else if (operation == LfsOperations.Add) { errorFormatString = Resources.Strings.FileSystem_InconsistencyError_Creating_Format; } else if (operation == LfsOperations.Update) { errorFormatString = Resources.Strings.FileSystem_InconsistencyError_Updating_Format; } throw new InconsistentFileSystemException(targetType, globalFileSystemNumber, errorFormatString); } fileSystem.Status = currentDirtyFlags; return(currentDirtyFlags); }
private static void ReformatFileSystem(AsyncTaskData taskData) { var data = (ExecuteDeviceCommandAsyncTaskData)taskData; var device = data.Device; data.Succeeded = SetDirtyFlags.Create(LfsDirtyFlags.FileSystemUpdateInProgress).Execute <bool>(device.Port, data); if (data.Succeeded) { data.Succeeded = Reformat.Instance.Execute <bool>(device.Port, data); } if (data.Succeeded) { data.Succeeded = SetDirtyFlags.Create(LfsDirtyFlags.None).Execute <bool>(device.Port, data); } if (data.Succeeded) { var fileSystem = DownloadFileSystemTables.Instance.Execute <FileSystem>(device.Port, data); if (data.Succeeded) { var fileSystemStatistics = Model.Commands.GetFileSystemStatistics.Instance.Execute <FileSystemStatistics>(device.Port, data); data.Result = new Tuple <FileSystem, FileSystemStatistics>(fileSystem, fileSystemStatistics); } } }
// NOTE: The specific task data (ExecuteDeviceCommandAsyncTaskData.Data) must be a Tuple<MenuLayout, bool, bool>. // The first Boolean value indicates whether the operation should attempt to force the removal of menu position data. // The second Boolean value indicates whether the operation should update the root file's names (device name, owner). private static void SyncHostToDevice(AsyncTaskData taskData) { var data = (ExecuteDeviceCommandAsyncTaskData)taskData; var device = data.Device; data.Task.UpdateTaskProgress(0, Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ComputingChangesProgress); var currentDirtyFlags = GetDirtyFlags.Instance.Execute <LfsDirtyFlags>(device.Port, data); var deviceFileSystem = DownloadFileSystemTables.Instance.Execute <FileSystem>(device.Port, data); deviceFileSystem.Status = currentDirtyFlags; if (data.AcceptCancelIfRequested()) { return; } var hostData = (Tuple <MenuLayout, bool, bool>)data.Data; var hostFileSystem = hostData.Item1.FileSystem; var resetSaveMenuPosition = hostData.Item2; if (resetSaveMenuPosition) { hostFileSystem.ForceRemovalOfMenuPositionData(deviceFileSystem); } hostFileSystem.SuppressRootFileNameDifferences(device); hostFileSystem.PopulateSaveDataForksFromDevice(deviceFileSystem); // The first pass gathers all differences, including errors. var allDifferencesWithErrors = hostFileSystem.CompareTo(deviceFileSystem, device, true); #if USE_SMART_COPY // Now, use a clone of the original file system to compute the actual work to do, and use the failures for a post-op error report. var hostFileSystemWorkingCopy = hostFileSystem.Clone(); var ignoreRootFileNames = !hostData.Item3; if (ignoreRootFileNames) { hostFileSystemWorkingCopy.Files[GlobalFileTable.RootDirectoryFileNumber].ShortName = deviceFileSystem.Files[GlobalFileTable.RootDirectoryFileNumber].ShortName; hostFileSystemWorkingCopy.Files[GlobalFileTable.RootDirectoryFileNumber].LongName = deviceFileSystem.Files[GlobalFileTable.RootDirectoryFileNumber].LongName; } var partialErrors = hostFileSystemWorkingCopy.CleanUpInvalidEntries(deviceFileSystem, allDifferencesWithErrors, FileSystemHelpers.ShouldRemoveInvalidEntry, ShouldIncludeError); var allDifferences = hostFileSystemWorkingCopy.CompareTo(deviceFileSystem, device, true); #else // This will try to copy all ROMs, even incompatible ones, and should eventually be removed. var hostFileSystemWorkingCopy = hostFileSystem; var partialErrors = allDifferencesWithErrors.GetAllFailures(ShouldIncludeError); var allDifferences = allDifferencesWithErrors; #endif // USE_SMART_COPY var succeeded = true; var phaseNumber = 0; var updateString = string.Empty; // Run any delete operations... var total = allDifferences.DirectoryDifferences.ToDelete.Count + allDifferences.FileDifferences.ToDelete.Count + allDifferences.ForkDifferences.ToDelete.Count + allDifferences.ForkDifferences.ToUpdate.Count; var numComplete = 0; if (data.AcceptCancelIfRequested()) { return; } if (total > 0) { ++phaseNumber; if (!data.CancelRequsted && succeeded && allDifferences.DirectoryDifferences.ToDelete.Any()) { var deleteOpData = new DeleteOperationData(data, LfsEntityType.Directory) { Factory = (gdn) => DeleteFolder.Create((byte)(uint)gdn[0]), UpdateTitleFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, UpdateTitleInfo = Resources.Strings.LfsOperation_Remove, UpdateProgressFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_DeletingItemProgressFormat, UpdateProgressInfo = Resources.Strings.DirectoriesInSentence, Phase = phaseNumber, Total = total, Current = numComplete }; currentDirtyFlags |= DeleteEntries(allDifferences.DirectoryDifferences.ToDelete, deleteOpData, currentDirtyFlags, deviceFileSystem); numComplete = deleteOpData.Current; succeeded = data.Succeeded; } if (!data.CancelRequsted && succeeded && allDifferences.FileDifferences.ToDelete.Any()) { var deleteOpData = new DeleteOperationData(data, LfsEntityType.File) { Factory = (gfn) => DeleteFile.Create((ushort)(uint)gfn[0]), UpdateTitleFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, UpdateTitleInfo = Resources.Strings.LfsOperation_Remove, UpdateProgressFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_DeletingItemProgressFormat, UpdateProgressInfo = Resources.Strings.FilesInSentence, Phase = phaseNumber, Total = total, Current = numComplete }; currentDirtyFlags |= DeleteEntries(allDifferences.FileDifferences.ToDelete, deleteOpData, currentDirtyFlags, deviceFileSystem); numComplete = deleteOpData.Current; succeeded = data.Succeeded; } if (!data.CancelRequsted && succeeded && (allDifferences.ForkDifferences.ToDelete.Any() || allDifferences.ForkDifferences.ToUpdate.Any())) { var deleteOpData = new DeleteOperationData(data, LfsEntityType.Fork) { Factory = (gkn) => DeleteFork.Create((ushort)(uint)gkn[0]), UpdateTitleFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, UpdateTitleInfo = Resources.Strings.LfsOperation_Remove, UpdateProgressFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_DeletingItemProgressFormat, UpdateProgressInfo = Resources.Strings.DeviceMultistageCommand_RemovingFileContents, Phase = phaseNumber, Total = total, Current = numComplete }; var forks = allDifferences.ForkDifferences.ToDelete.ToList(); var updatedForksToTreatAsDeletions = allDifferences.ForkDifferences.ToUpdate.Select(f => (uint)f.GlobalForkNumber).Where(f => !allDifferences.ForkDifferences.FailedOperations.Any(o => (o.Value.GlobalFileSystemNumber == f) && FileSystemHelpers.IsMissingForkError(o.Value))); forks.AddRange(updatedForksToTreatAsDeletions); currentDirtyFlags |= DeleteEntries(forks, deleteOpData, currentDirtyFlags, deviceFileSystem); numComplete = deleteOpData.Current; succeeded = data.Succeeded; } } if (data.AcceptCancelIfRequested()) { return; } data.CurrentlyExecutingCommand = ProtocolCommandId.UnknownCommand; data.FailureMessage = Resources.Strings.SyncHostToDeviceCommand_GatherUpdatesFailedMessage; var updateOperations = hostFileSystemWorkingCopy.GatherAllUpdates(allDifferences, device.UniqueId); if (updateOperations.Any()) { do { if (data.AcceptCancelIfRequested()) { break; } LfsUpdateOperation operation = null; data.FailureMessage = Resources.Strings.SyncHostToDevice_FetchUpdateOperationFailedMessage; updateOperations = updateOperations.FetchUpdateOperation(out operation); if (data.AcceptCancelIfRequested()) { break; } ++phaseNumber; numComplete = 0; total = operation.Forks.Count + operation.Files.Count + operation.Directories.Count; foreach (var failure in operation.Failures) { partialErrors[failure.Description] = failure.Exception; } // Load forks. var address = 0u; if (!data.CancelRequsted && succeeded && operation.Forks.Any()) { data.FailureMessage = null; var uploadOpData = new UploadDataOperationData(data, address) { TargetType = LfsEntityType.Fork, Factory = (upl) => UploadDataBlockToRam.Create((uint)upl[0], (ByteSerializer)upl[1], (uint)upl[2]), GetSerializer = FileSystemHelpers.ToForkByteSerializer, ShouldUpdateProgress = (k) => true, UpdateTitleFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, UpdateTitleInfo = operation.ProgressTitle, UpdateProgressFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_TransferToRamProgressFormat, Phase = phaseNumber, Total = total, Current = numComplete }; address = UploadEntries(operation.FileSystem.Forks, operation.Forks, uploadOpData); numComplete = uploadOpData.Current; succeeded = data.Succeeded; } // Load GFT update. if (!data.CancelRequsted && succeeded && operation.Files.Any()) { data.FailureMessage = null; var fileRange = operation.GfnRange; var files = Enumerable.Range(fileRange.Minimum, (fileRange.Maximum - fileRange.Minimum) + 1); // slower than a loop, but who cares? var uploadOpData = new UploadDataOperationData(data, address) { TargetType = LfsEntityType.File, Factory = (upl) => UploadDataBlockToRam.Create((uint)upl[0], (ByteSerializer)upl[1], (uint)upl[2]), GetSerializer = FileSystemHelpers.ToFileByteSerializer, ShouldUpdateProgress = (f) => (f != null) && operation.Files.Contains(((ILfsFileInfo)f).GlobalFileNumber), UpdateTitleFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, UpdateTitleInfo = operation.ProgressTitle, UpdateProgressFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_TransferToRamProgressFormat, Phase = phaseNumber, Total = total, Current = numComplete }; // THIS COULD BE DONE WITH ONE LARGE BLOCK INSTEAD OF MANY SMALLER ONES address = UploadEntries(operation.FileSystem.Files, files, uploadOpData); numComplete = uploadOpData.Current; succeeded = data.Succeeded; } // Load GDT update. if (!data.CancelRequsted && succeeded && operation.Directories.Any()) { data.FailureMessage = null; var directoryRange = operation.GdnRange; var directories = Enumerable.Range(directoryRange.Minimum, (directoryRange.Maximum - directoryRange.Minimum) + 1); // slower than a loop, but who cares? var uploadOpData = new UploadDataOperationData(data, address) { TargetType = LfsEntityType.Directory, Factory = (upl) => UploadDataBlockToRam.Create((uint)upl[0], (ByteSerializer)upl[1], (uint)upl[2]), GetSerializer = FileSystemHelpers.ToDirectoryByteSerializer, ShouldUpdateProgress = (d) => (d != null) && operation.Files.Contains(((IDirectory)d).GlobalDirectoryNumber), UpdateTitleFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, UpdateTitleInfo = operation.ProgressTitle, UpdateProgressFormat = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_TransferToRamProgressFormat, Phase = phaseNumber, Total = total, Current = numComplete }; // THIS COULD BE DONE WITH ONE LARGE BLOCK INSTEAD OF MANY SMALLER ONES address = UploadEntries(operation.FileSystem.Directories, directories, uploadOpData); numComplete = uploadOpData.Current; succeeded = data.Succeeded; } if (data.AcceptCancelIfRequested()) { break; } // Everything is in RAM now, so execute commands. address = 0; // All the data was initially loaded at location zero. numComplete = 0; total = operation.Forks.Count; if (operation.Files.Count > 0) { ++total; } if (operation.Directories.Count > 0) { ++total; } // Create forks. if (!data.CancelRequsted && succeeded && operation.Forks.Any()) { data.FailureMessage = null; var numCreated = 0; var numToCreate = operation.Forks.Count; foreach (var gkn in operation.Forks) { if (data.AcceptCancelIfRequested()) { break; } address = address.Align(); var fork = operation.FileSystem.Forks[gkn]; updateString = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.DeviceMultistageCommand_UpdatingFiles_ProgressTitleFormat, operation.ProgressTitle, ++numComplete, total, phaseNumber); data.Task.UpdateTaskTitle(updateString); updateString = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.DeviceMultistageCommand_UpdatingFiles_CreatingItemProgressFormat, fork.Name, ++numCreated, numToCreate); data.Task.UpdateTaskProgress((double)numCreated / numToCreate, updateString); currentDirtyFlags |= currentDirtyFlags.UpdateFileSystemDirtyState(deviceFileSystem, data, gkn, LfsOperations.Add, LfsEntityType.Fork); CreateForkFromRam.Create(address, fork).Execute(device.Port, data, out succeeded); address += (uint)fork.SerializeByteCount; } address = address.Align(); } // Update GFT. if (!data.CancelRequsted && succeeded && operation.Files.Any()) { data.FailureMessage = null; if (data.AcceptCancelIfRequested()) { break; } var gftRange = operation.GfnRange; if (operation.Files.Count == 1) { updateString = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.DeviceMultistageCommand_UpdatingFileSystemTablesOneEntry_ProgressTitleFormat, Resources.Strings.File, gftRange.Minimum, phaseNumber); } else { updateString = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.DeviceMultistageCommand_UpdatingFileSystemTables_ProgressTitleFormat, Resources.Strings.File, gftRange.Minimum, gftRange.Maximum, phaseNumber); } data.Task.UpdateTaskTitle(updateString); data.Task.UpdateTaskProgress((double)(++numComplete) / total, Resources.Strings.DeviceMultistageCommand_UpdatingGlobalFilesTable); currentDirtyFlags |= currentDirtyFlags.UpdateFileSystemDirtyState(deviceFileSystem, data, gftRange.Minimum, LfsOperations.Update, LfsEntityType.File); UpdateGlobalFileTable.Create(address, gftRange).Execute(device.Port, data, out succeeded); address += (uint)(gftRange.Maximum - gftRange.Minimum + 1) * LfsFileInfo.FlatSizeInBytes; } // Update GDT. if (!data.CancelRequsted && succeeded && operation.Directories.Any()) { data.FailureMessage = null; if (data.AcceptCancelIfRequested()) { break; } var gdtRange = operation.GdnRange; if (operation.Directories.Count == 1) { updateString = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.DeviceMultistageCommand_UpdatingFileSystemTablesOneEntry_ProgressTitleFormat, Resources.Strings.Directory, gdtRange.Minimum, phaseNumber); } else { updateString = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.DeviceMultistageCommand_UpdatingFileSystemTables_ProgressTitleFormat, Resources.Strings.Directory, gdtRange.Minimum, gdtRange.Maximum, phaseNumber); } data.Task.UpdateTaskTitle(updateString); data.Task.UpdateTaskProgress((double)(++numComplete) / total, Resources.Strings.DeviceMultistageCommand_UpdatingGlobalDirectoriesTable); currentDirtyFlags |= currentDirtyFlags.UpdateFileSystemDirtyState(deviceFileSystem, data, gdtRange.Minimum, LfsOperations.Update, LfsEntityType.Directory); UpdateGlobalDirectoryTable.Create(address, gdtRange).Execute(device.Port, data, out succeeded); address += (uint)(gdtRange.Maximum - gdtRange.Minimum + 1) * Directory.FlatSizeInBytes; } }while (!data.CancelRequsted && succeeded && updateOperations.Any()); } if (!data.CancelRequsted && data.Succeeded) { SetDirtyFlags.Create(LfsDirtyFlags.None).Execute(device.Port, data, out succeeded); deviceFileSystem.Status = LfsDirtyFlags.None; } if (!data.CancelRequsted && data.Succeeded) { var updatedFileSystem = DownloadFileSystemTables.Instance.Execute(device.Port, data, out succeeded) as FileSystem; #if USE_SMART_COPY partialErrors = allDifferences.CombineAllFailures(partialErrors, ShouldIncludeError); #endif // USE_SMART_COPY data.Result = new Tuple <FileSystem, IDictionary <string, FailedOperationException> >(updatedFileSystem, partialErrors); } }