private static void UpdateDeviceOwner(AsyncTaskData taskData)
        {
            // Skip setting dirty flags on this -- is that too risky?
            var data     = (ExecuteDeviceCommandAsyncTaskData)taskData;
            var device   = data.Device;
            var newOwner = data.Data as string;
            var rootFile = device.FileSystem.Files[GlobalFileTable.RootDirectoryFileNumber];

            rootFile.LongName = newOwner;
            var succeeded = false;

            data.Result = UploadDataBlockToRam.Create(0, FileSystemHelpers.ToFileByteSerializer(rootFile), 0).Execute(device.Port, data, out succeeded);
            if (succeeded)
            {
                data.Result = UpdateGlobalFileTable.Create(0, new[] { rootFile }).Execute(device.Port, data, out succeeded);
            }
        }
        // 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);
            }
        }
Example #3
0
        private static void UpdateFirmware(AsyncTaskData taskData)
        {
            var data               = (ExecuteDeviceCommandAsyncTaskData)taskData;
            var device             = data.Device;
            var customData         = data.Data as Tuple <string, int>;
            var firmwareUpdateFile = customData.Item1;
            var succeeded          = false;

            UploadDataBlockToRam.Create(0, new FileSystemFile(firmwareUpdateFile), 0).Execute(device.Port, data, out succeeded);
            if (succeeded)
            {
                ValidateFirmwareImageInRam.Instance.Execute(device.Port, data, out succeeded);
                if (succeeded)
                {
                    // Check to see if there's an error database file. If so, and it's different than the one
                    // we've cached in the FirmwareUpdates directory, copy it over so we'll have a matching error_db.yaml.
                    // We're not going to complain if this part fails, so sit on any exceptions that may happen.
                    try
                    {
                        var configuration = Configuration.Instance;
                        var sourceErrorDatabaseFilePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(firmwareUpdateFile), ErrorLog.DefaultErrorDatabaseName);
                        if (System.IO.File.Exists(sourceErrorDatabaseFilePath))
                        {
                            if (!System.IO.Directory.Exists(configuration.FirmwareUpdatesDirectory))
                            {
                                System.IO.Directory.CreateDirectory(configuration.FirmwareUpdatesDirectory);
                            }
                            var destinationErrorDatabaseFilePath = System.IO.Path.Combine(configuration.FirmwareUpdatesDirectory, ErrorLog.DefaultErrorDatabaseName);
                            var crcOfSource      = INTV.Core.Utility.Crc32.OfFile(sourceErrorDatabaseFilePath);
                            var crcOfDestination = 0u;
                            if (System.IO.File.Exists(destinationErrorDatabaseFilePath))
                            {
                                crcOfDestination = INTV.Core.Utility.Crc32.OfFile(destinationErrorDatabaseFilePath);
                                if (crcOfSource != crcOfDestination)
                                {
                                    var backupPath = destinationErrorDatabaseFilePath.GetUniqueBackupFilePath();
                                    System.IO.File.Move(destinationErrorDatabaseFilePath, backupPath);
                                }
                            }
                            if ((crcOfDestination == 0) || (crcOfSource != crcOfDestination))
                            {
                                System.IO.File.Copy(sourceErrorDatabaseFilePath, destinationErrorDatabaseFilePath);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine("Failed to copy error database file: " + e);
                    }
                }
                else
                {
                    data.FailureMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.UpdateFirmwareCommand_ValidationFailedFormat, firmwareUpdateFile);
                }
            }
            else
            {
                data.FailureMessage = Resources.Strings.DeviceMultistageCommand_UpdateFirmware_DownloadFailed;
            }
            if (succeeded && device.ShouldRemoveSecondaryFirmware(data, out succeeded))
            {
                device.RemoveSecondaryFirmware(data, out succeeded);
            }
            if (succeeded && !device.ShouldRemoveSecondaryFirmware(data, out succeeded))
            {
                // Secondary is gone -- so now update firmware!
                ProgramSecondaryFirmware.Instance.Execute(device.Port, data, out succeeded);
                data.UpdateTaskProgress(0, Resources.Strings.ProtocolCommandId_WaitForBeaconPseudoCommand_Title);
                succeeded = device.WaitForBeacon(ProtocolCommand.WaitForBeaconTimeout * 2) && succeeded;
            }
            if (succeeded)
            {
                var newRevisions = QueryFirmwareRevisions.Instance.Execute(device.Port, data, out succeeded) as FirmwareRevisions;
                if (succeeded)
                {
                    var deployedVersion = newRevisions.Current;
                    succeeded = deployedVersion == customData.Item2;
                }
            }
            if (!succeeded && string.IsNullOrWhiteSpace(data.FailureMessage))
            {
                data.FailureMessage = Resources.Strings.DeviceMultistageCommand_UpdateFirmware_Failed;
            }
        }