Пример #1
0
        private static bool ShouldRemoveSecondaryFirmware(this Device device, ExecuteDeviceCommandAsyncTaskData taskData, out bool succeeded)
        {
            var firmwareRevisions = QueryFirmwareRevisions.Instance.Execute(device.Port, taskData, out succeeded) as FirmwareRevisions;
            var shouldRemove      = succeeded && (firmwareRevisions.Secondary != FirmwareRevisions.UnavailableFirmwareVersion);

            return(shouldRemove);
        }
        private static LuigiFileHeader GetLuigiHeaderForFork(ExecuteDeviceCommandAsyncTaskData data, Fork fork)
        {
            LuigiFileHeader luigiHeader = null;

            using (var memory = new System.IO.MemoryStream())
            {
                const uint Address     = 0u;
                var        bytesToRead = LuigiFileHeader.MaxHeaderSize;
                var        succeeded   = ReadForkToRam.Create(Address, fork.GlobalForkNumber, 0u, bytesToRead).Execute <bool>(data.Device.Port, data);
                byte[]     dataRead    = null;
                if (succeeded)
                {
                    dataRead = (byte[])DownloadDataBlockFromRam.Create(Address, bytesToRead).Execute(data.Device.Port, data, out succeeded);
                }
                if (succeeded)
                {
                    memory.Write(dataRead, 0, bytesToRead);
                    memory.Seek(0, System.IO.SeekOrigin.Begin);
                    try
                    {
                        luigiHeader = LuigiFileHeader.Inflate(memory);
                    }
                    catch (INTV.Core.UnexpectedFileTypeException)
                    {
                    }
                }
            }
            return(luigiHeader);
        }
Пример #3
0
        private static object RemoveSecondaryFirmware(this Device device, ExecuteDeviceCommandAsyncTaskData taskData, out bool succeeded)
        {
            var result = EraseSecondaryFirmware.Instance.Execute(device.Port, taskData, out succeeded);

            if (succeeded)
            {
                device.WaitForBeacon(ProtocolCommand.WaitForBeaconTimeout * 2);
            }
            return(result);
        }
 /// <summary>
 /// Retrieves the data associated with forks from a Locutus device.
 /// </summary>
 /// <param name="device">The device upon which the data resides.</param>
 /// <param name="forksToRetrieve">The data forks to retrieve.</param>
 public static void RetrieveForkData(this Device device, IEnumerable <Fork> forksToRetrieve)
 {
     if (forksToRetrieve.Any() && device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
         {
             Data = forksToRetrieve
         };
         executeCommandTaskData.StartTask(RetrieveForkData);
     }
 }
Пример #5
0
 /// <summary>
 /// Gets the file system dirty flags from a Locutus device.
 /// </summary>
 /// <param name="device">The target Locutus device whose file system dirty flags are to be retrieved.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void GetFileSystemFlags(this Device device, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.LfsGetFileSystemStatusFlags)
         {
             OnSuccess = (c, p, r) => device.FileSystemFlags = (LfsDirtyFlags)r,
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(GetFileSystemFlags);
     }
 }
Пример #6
0
 /// <summary>
 /// Remove the secondary firmware from the device (i.e. revert to factory version.).
 /// </summary>
 /// <param name="device">Target of the command.</param>
 /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void RemoveSecondaryFirmware(this Device device, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.FirmwareEraseSecondary)
         {
             OnSuccess = (c, p, r) => device.GetFirmwareRevisions(onCompleteHandler, errorHandler),
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(RemoveSecondaryFirmware);
     }
 }
 /// <summary>
 /// Updates the device configuration settings.
 /// </summary>
 /// <param name="device">The target Locutus device whose configuration is to be set.</param>
 /// <param name="newConfigurationFlags">The new configuration data.</param>
 /// <param name="onCompleteHandler">Success handler, used to report successful execution of the command.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void SetConfiguration(this Device device, DeviceStatusFlags newConfigurationFlags, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.SetConfiguration)
         {
             Data      = newConfigurationFlags,
             OnSuccess = onCompleteHandler,
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(SetConfiguration);
     }
 }
Пример #8
0
 /// <summary>
 /// Update firmware on the device.
 /// </summary>
 /// <param name="device">Target of the command.</param>
 /// <param name="firmwarePath">Path on local file system to the firmware update.</param>
 /// <param name="newVersion">New version of the firmware, used for validation.</param>
 /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void UpdateFirmware(this Device device, string firmwarePath, int newVersion, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var data = new Tuple <string, int>(firmwarePath, newVersion);
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
         {
             Title     = Resources.Strings.DeviceMultistageCommand_UpdateFirmware_Title,
             Data      = data,
             OnSuccess = (c, p, r) => device.GetFirmwareRevisions(onCompleteHandler, errorHandler),
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(UpdateFirmware, 0.55);
     }
 }
 /// <summary>
 /// Sets the device's owner.
 /// </summary>
 /// <param name="device">The device whose owner is to be set.</param>
 /// <param name="newOwner">The new owner for the device.</param>
 /// <param name="errorHandler">Error handler to report any problems.</param>
 public static void UpdateDeviceOwner(this Device device, string newOwner, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
         {
             Title          = Resources.Strings.DeviceMultistageCommand_UpdateDeviceOwner_Title,
             FailureMessage = Resources.Strings.SetDeviceOwnerCommand_ErrorMessage,
             OnFailure      = errorHandler,
             OnSuccess      = (c, p, r) => device.Owner = newOwner,
             Data           = newOwner
         };
         executeCommandTaskData.StartTask(UpdateDeviceOwner);
     }
 }
Пример #10
0
 /// <summary>
 /// Downloads a ROM to Locutus to be executed immediately.
 /// </summary>
 /// <param name="device">The target Locutus device to receive and execute the given ROM.</param>
 /// <param name="programRom">The ROM to play.</param>
 /// <param name="programName">The friendly name of the program to run.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void DownloadAndPlay(this Device device, IRom programRom, string programName, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.DownloadAndPlay)
         {
             Title = string.Format(Resources.Strings.DeviceCommand_DownloadAndPlay_TitleFormat, programName),
             ProgressUpdateMode = ExecuteDeviceCommandProgressUpdateMode.Custom,
             FailureMessage     = string.Format(Resources.Strings.DeviceCommand_DownloadAndPlay_FailedFormat, programName),
             OnFailure          = errorHandler,
             Data = programRom
         };
         executeCommandTaskData.StartTask(DownloadAndPlay);
     }
 }
Пример #11
0
 /// <summary>
 /// Executes a backup of the file system on a Locutus device.
 /// </summary>
 /// <param name="device">The device whose data is to be backed up.</param>
 /// <param name="backupDirectory">The directory in which to do the backup.</param>
 /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
 /// <param name="errorHandler">Function to call if an error occurs. If it returns <c>true</c>, the error is considered handled</param>
 public static void BackupFileSystem(this Device device, string backupDirectory, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
         {
             Title = Resources.Strings.DeviceMultistageCommand_BackupFileSystem_Title,
             ProgressUpdateMode = ExecuteDeviceCommandProgressUpdateMode.Custom,
             Data      = backupDirectory,
             OnSuccess = onCompleteHandler,
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(BackupFileSystem, true, 0.5);
     }
 }
        private static string GetSourcePathForFork(ExecuteDeviceCommandAsyncTaskData data, Fork fork, FileSystem deviceFileSystem, IEnumerable <ProgramDescription> roms, RomListConfiguration romsConfiguration)
        {
            bool   retrievalNecessary;
            string destinationDir = null;
            var    forkSourcePath = GetPathForFork(data, fork, deviceFileSystem, roms, romsConfiguration, ref destinationDir, out retrievalNecessary);

            if (retrievalNecessary)
            {
                if (!data.Device.RetrieveForkData(data, new[] { fork }, destinationDir, new[] { System.IO.Path.GetFileName(forkSourcePath) }))
                {
                    var syncErrors = data.Result as FileSystemSyncErrors;
                    syncErrors.UnableToRetrieveForks.Add(fork);
                    forkSourcePath = null;
                }
            }
            return(forkSourcePath);
        }
 /// <summary>
 /// Synchronizes the host system's MenuLayout to match the contents of a Locutus device.
 /// </summary>
 /// <param name="device">The Locutus device whose file system is to be imposed upon the Menu Layout of the host PC.</param>
 /// <param name="hostMenuLayout">The host PC MenuLayout to be brought in sync with Locutus.</param>
 /// <param name="ignoreInconsistentFileSystem">If <c>true</c> and the device's file system is in an inconsistent state, do the sync anyway.</param>
 /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void SyncDeviceToHost(this Device device, MenuLayout hostMenuLayout, bool ignoreInconsistentFileSystem, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         ////var configuration = SingleInstanceApplication.Instance.GetConfiguration<Configuration>();
         var customData             = new Tuple <MenuLayout, bool>(hostMenuLayout, ignoreInconsistentFileSystem);
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
         {
             Title = Resources.Strings.DeviceMultistageCommand_SyncingToFiles_Title,
             ProgressUpdateMode = ExecuteDeviceCommandProgressUpdateMode.Custom,
             Data      = customData,
             OnSuccess = onCompleteHandler,
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(SyncDeviceToHost, true, 1);
     }
 }
        /// <summary>
        /// Downloads the host system's MenuLayout file system content to a Locutus device.
        /// </summary>
        /// <param name="device">The target Locutus device whose file system is to be updated to match that of the host PC.</param>
        /// <param name="hostMenuLayout">The host PC MenuLayout to push down to Locutus.</param>
        /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
        /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
        public static void SyncHostToDevice(this Device device, MenuLayout hostMenuLayout, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
        {
            if (device.IsSafeToStartCommand())
            {
                var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
                {
                    Title = Resources.Strings.DeviceMultistageCommand_UpdatingFiles_Title,
                    ProgressUpdateMode = ExecuteDeviceCommandProgressUpdateMode.Custom,

                    // First bool is whether to reset menu position data (true => REMOVE IT).
                    // Second bool is whether to update root file name (false => RETAIN IT).
                    Data      = new Tuple <MenuLayout, bool, bool>(hostMenuLayout, true, false),
                    OnSuccess = onCompleteHandler,
                    OnFailure = errorHandler
                };
                executeCommandTaskData.StartTask(SyncHostToDevice, true, 1);
            }
        }
Пример #15
0
 public static void GetErrorLog(this Device device, Action <ErrorLog> onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.DownloadErrorLog)
         {
             OnSuccess = (c, p, r) =>
             {
                 device.ErrorLog = (ErrorLog)r;
                 if (onCompleteHandler != null)
                 {
                     onCompleteHandler(device.ErrorLog);
                 }
             },
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(GetErrorLog);
     }
 }
Пример #16
0
 /// <summary>
 /// Get the factory, active, and secondary firmware versions.
 /// </summary>
 /// <param name="device">Target of the command.</param>
 /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void GetFirmwareRevisions(this Device device, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.FirmwareGetRevisions)
         {
             OnSuccess = (c, p, r) =>
             {
                 device.FirmwareRevisions = (FirmwareRevisions)r;
                 if (onCompleteHandler != null)
                 {
                     onCompleteHandler(c, p, r);
                 }
             },
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(GetFirmwareRevisions);
     }
 }
        private static string GetRomPathForForkFromRomList(ExecuteDeviceCommandAsyncTaskData data, Fork fork, IEnumerable <ProgramDescription> roms, string destDir, out uint crc32, out uint cfgCrc32)
        {
            string romPath = null;

            crc32    = 0;
            cfgCrc32 = 0;
            var luigiHeader = GetLuigiHeaderForFork(data, fork);

            if ((luigiHeader != null) && (luigiHeader.Version > 0))
            {
                crc32    = luigiHeader.OriginalRomCrc32;
                cfgCrc32 = luigiHeader.OriginalCfgCrc32;
                var rom = roms.FirstOrDefault(r => (r.Crc == luigiHeader.OriginalRomCrc32) && ((luigiHeader.OriginalCfgCrc32 == 0) || (luigiHeader.OriginalCfgCrc32 == r.Rom.CfgCrc)));
                if (rom != null)
                {
                    romPath = rom.Rom.RomPath;
                }
            }
            return(romPath);
        }
        /// <summary>
        /// Retrieves the file system from a Locutus device.
        /// </summary>
        /// <param name="device">The device whose file system is being retrieved.</param>
        /// <param name="operationTitle">Title to show in the progress bar.</param>
        /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
        /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
        public static void RetrieveFileSystem(this Device device, string operationTitle, DeviceCommandCompleteHandler onCompleteHandler, DeviceCommandErrorHandler errorHandler)
        {
            if (device.IsSafeToStartCommand())
            {
                var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.LfsDownloadGlobalTables)
                {
                    Title = operationTitle,

                    OnFailure = errorHandler
                };
                executeCommandTaskData.OnSuccess = (c, p, r) =>
                {
                    device.FileSystem = (FileSystem)r;
                    if (onCompleteHandler != null)
                    {
                        onCompleteHandler(c, p, r);
                    }
                };
                executeCommandTaskData.StartTask(RetrieveFileSystem, 1.5);
            }
        }
Пример #19
0
 /// <summary>
 /// Reformats the file system on a Locutus device.
 /// </summary>
 /// <param name="device">The target Locutus device whose file system is to be reformatted.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 /// <param name="onComplete">Action to execute when reformat operation is complete. This value may be <c>null</c>.</param>
 public static void ReformatFileSystem(this Device device, DeviceCommandErrorHandler errorHandler, Action onComplete)
 {
     if (device.IsSafeToStartCommand())
     {
         var reformatFileSystemTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.LfsReformatFileSystem)
         {
             OnSuccess = (c, p, r) =>
             {
                 var results = (Tuple <FileSystem, FileSystemStatistics>)r;
                 device.FileSystem           = results.Item1;
                 device.FileSystemStatistics = results.Item2;
                 if (onComplete != null)
                 {
                     onComplete();
                 }
             },
             OnFailure = errorHandler
         };
         reformatFileSystemTaskData.StartTask(ReformatFileSystem);
     }
 }
Пример #20
0
 /// <summary>
 /// Retrieves the error and crash logs as needed from the target device.
 /// </summary>
 /// <param name="device">Target of the command.</param>
 /// <param name="onCompleteHandler">Called upon successful completion of the operation. This argument may be <c>null</c>.</param>
 /// <param name="errorHandler">Error handler, used to report errors to the user.</param>
 public static void GetErrorAndCrashLogs(this Device device, Action <ErrorLog, CrashLog> onCompleteHandler, DeviceCommandErrorHandler errorHandler)
 {
     if (device.IsSafeToStartCommand())
     {
         var executeCommandTaskData = new ExecuteDeviceCommandAsyncTaskData(device, ProtocolCommandId.MultistagePseudoCommand)
         {
             Title     = Resources.Strings.DeviceMultistageCommand_GetErrorAndCrashLogs_Title,
             OnSuccess = (c, p, r) =>
             {
                 var results = (Tuple <ErrorLog, CrashLog>)r;
                 if (onCompleteHandler != null)
                 {
                     onCompleteHandler(results.Item1, results.Item2);
                 }
                 device.ErrorLog = results.Item1;
                 device.CrashLog = results.Item2;
             },
             OnFailure = errorHandler
         };
         executeCommandTaskData.StartTask(GetErrorAndCrashLogs);
     }
 }
 /// <summary>
 /// Initializes a new instance of FileSystemOperationData.
 /// </summary>
 /// <param name="operation">The kind of operation being performed.</param>
 /// <param name="taskData">Task data used for error reporting, et. al.</param>
 protected FileSystemOperationData(LfsOperations operation, ExecuteDeviceCommandAsyncTaskData taskData)
 {
     Operation = operation;
     TaskData  = taskData;
 }
        private static bool RetrieveForkData(this Device device, ExecuteDeviceCommandAsyncTaskData data, IEnumerable <Fork> forksToRetrieve, string destinationDirectory, IEnumerable <string> fileNames)
        {
            var configuration      = SingleInstanceApplication.Instance.GetConfiguration <Configuration>();
            var succeeded          = false;
            var numForks           = forksToRetrieve.Count();
            var forkFilenames      = fileNames == null ? null : fileNames.ToList();
            var forkBeingRetrieved = 0;

            foreach (var fork in forksToRetrieve)
            {
                if (data.AcceptCancelIfRequested())
                {
                    break;
                }
                ++forkBeingRetrieved;
                if (data != null)
                {
                    data.UpdateTaskProgress((double)forkBeingRetrieved / numForks, string.Format(Resources.Strings.DeviceMultistageCommand_BackupFileSystem_RetrievingFiles_Format, forkBeingRetrieved, numForks));
                }
                var bytesRemaining = (int)fork.Size;
                var offset         = 0;
                succeeded = false;
                using (var memory = new System.IO.MemoryStream())
                {
                    do
                    {
                        const uint Address     = 0u;
                        var        bytesToRead = System.Math.Min(bytesRemaining, Device.TotalRAMSize);
                        succeeded = ReadForkToRam.Create(Address, fork.GlobalForkNumber, (uint)offset, bytesToRead).Execute <bool>(device.Port, data);
                        byte[] dataRead = null;
                        if (succeeded)
                        {
                            dataRead = (byte[])DownloadDataBlockFromRam.Create(Address, bytesToRead).Execute(device.Port, data, out succeeded);
                        }
                        if (succeeded)
                        {
                            memory.Write(dataRead, offset, bytesToRead);
                            bytesRemaining -= bytesToRead;
                            offset         += bytesToRead;
                        }
                    }while (succeeded && (bytesRemaining > 0));

                    if (data != null)
                    {
                        data.UpdateTaskProgress((double)forkBeingRetrieved / numForks, string.Format(Resources.Strings.DeviceMultistageCommand_BackupFileSystem_SavingFiles_Format, forkBeingRetrieved, numForks));
                    }
                    memory.Seek(0, System.IO.SeekOrigin.Begin);
                    using (var tempFile = FileSystemFile.Inflate(memory))
                    {
                        var fileName = (forkFilenames == null) ? configuration.GetForkDataFileName(fork.GlobalForkNumber) : forkFilenames[forkBeingRetrieved - 1];
                        var forkPath = System.IO.Path.Combine(destinationDirectory, fileName);
                        forkPath            = forkPath.EnsureUniqueFileName();
                        data.FailureMessage = forkPath;
                        System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(forkPath));
                        try
                        {
                            // Test to see if this is a Fork containing a LUIGI file.
                            memory.Seek(0, System.IO.SeekOrigin.Begin);
                            LuigiFileHeader.Inflate(memory);
                            forkPath = System.IO.Path.ChangeExtension(forkPath, ProgramFileKind.LuigiFile.FileExtension());
                        }
                        catch (INTV.Core.UnexpectedFileTypeException)
                        {
                            // This is OK... we only want to execute certain functions if the file is a LUIGI file.
                        }
                        if (System.IO.File.Exists(forkPath))
                        {
                            var crcOfTarget = Crc32.OfFile(forkPath);
                            var crcOfSource = Crc32.OfFile(tempFile.FileInfo.FullName);
                            if (crcOfTarget != crcOfSource)
                            {
                                forkPath = forkPath.EnsureUniqueFileName();
                                System.IO.File.Copy(tempFile.FileInfo.FullName, forkPath);
                            }
                        }
                        else
                        {
                            System.IO.File.Copy(tempFile.FileInfo.FullName, forkPath);
                        }
                        fork.FilePath = forkPath;
                    }
                }
                if (!succeeded)
                {
                    break;
                }
            }
            return(succeeded);
        }
        private static string GetPathForFork(ExecuteDeviceCommandAsyncTaskData data, Fork fork, FileSystem deviceFileSystem, IEnumerable <ProgramDescription> roms, RomListConfiguration romsConfiguration, ref string destinationDir, out bool retrievalNecessary)
        {
            retrievalNecessary = false;
            string forkPath     = null;
            var    forkFileKind = ProgramFileKind.None;
            var    crc          = 0u;
            var    cfgCrc       = 0u;
            var    errors       = data.Result as FileSystemSyncErrors;

            // Determine what kind of fork this is.
            ILfsFileInfo fileContainingFork = null;
            var          forkKind           = deviceFileSystem.GetForkKind(fork, out fileContainingFork);

            switch (forkKind)
            {
            case ForkKind.Program:
                // Try to fetch LUIGI header from the fork.
                forkFileKind = ProgramFileKind.LuigiFile;
                forkPath     = GetRomPathForForkFromRomList(data, fork, roms, romsConfiguration.RomsDirectory, out crc, out cfgCrc);
                if (forkPath == null)
                {
                    forkPath = GetRomPathForForkFromCache(fork, romsConfiguration.RomsDirectory);
                }
                if ((forkPath == null) && string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = romsConfiguration.RomsDirectory;
                }
                break;

            case ForkKind.JlpFlash:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = romsConfiguration.RomsDirectory;     // seems sensible to keep save data file(s) next to the ROM
                }
                forkFileKind = ProgramFileKind.SaveData;
                break;

            case ForkKind.Manual:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = romsConfiguration.ManualsDirectory;
                }
                forkFileKind = ProgramFileKind.ManualText;
                break;

            case ForkKind.Vignette:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = Configuration.Instance.VignetteDataAreaPath;     // keep next to ROM?
                }
                forkFileKind = ProgramFileKind.Vignette;
                break;

            case ForkKind.Reserved4:
            case ForkKind.Reserved5:
            case ForkKind.Reserved6:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = Configuration.Instance.ReservedDataAreaPath;     // keep next to ROM?
                }
                forkFileKind = ProgramFileKind.GenericSupportFile;
                errors.UnsupportedForks.Add(new Tuple <ILfsFileInfo, Fork>(fileContainingFork, fork));
                ////throw new UnsupportedForkKindException(forkKind);
                break;

            case ForkKind.None:
                // An orphaned fork. Retrieve it, but we can't really do much with it.
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = Configuration.Instance.RecoveredDataAreaPath;     // orphaned fork
                }
                forkFileKind = ProgramFileKind.None;
                errors.OrphanedForks.Add(fork);
                break;

            default:
                throw new UnsupportedForkKindException(forkKind);
            }

            if ((destinationDir != null) && (forkPath == null))
            {
                retrievalNecessary = true;
                var forkFileBaseName = (fileContainingFork == null) ? Configuration.Instance.GetForkDataFileName(fork.GlobalForkNumber) : fileContainingFork.LongName.EnsureValidFileName();
                var extension        = forkFileKind.FileExtension();

                // For the menu position fork, use the default extension; since it's in the manual fork slot, we want
                // to override the .txt extension.
                if (string.IsNullOrEmpty(extension) || (fork.Uid == Fork.MenuPositionForkUid))
                {
                    extension = Configuration.ForkExtension;
                }
                var forkFileName = System.IO.Path.ChangeExtension(forkFileBaseName, extension);
                var destFile     = System.IO.Path.Combine(destinationDir, forkFileName);
                if (System.IO.File.Exists(destFile))
                {
                    var existingCrc    = 0u;
                    var existingCfgCrc = 0u;
                    var luigiHeader    = LuigiFileHeader.GetHeader(destFile);
                    if ((luigiHeader != null) && (luigiHeader.Version > 0))
                    {
                        existingCrc    = luigiHeader.OriginalRomCrc32;
                        existingCfgCrc = luigiHeader.OriginalCfgCrc32;
                    }
                    if (existingCrc == 0)
                    {
                        existingCrc = Crc32.OfFile(destFile);
                    }
                    if (existingCfgCrc == 0)
                    {
                        var destCfgFile = System.IO.Path.ChangeExtension(destFile, ProgramFileKind.CfgFile.FileExtension());
                        if (System.IO.File.Exists(destCfgFile))
                        {
                            existingCfgCrc = Crc32.OfFile(destCfgFile);
                        }
                    }

                    // This is the equivalent of RomComparerStrict: We skip retrieval only if both the ROM CRCs match and, if available, the .cfg CRCs match.
                    if ((crc != 0) && (existingCrc == crc) && ((cfgCrc == 0) || (existingCfgCrc == cfgCrc)))
                    {
                        retrievalNecessary = false;
                        forkPath           = destFile;
                    }
                    else
                    {
                        forkPath = destFile.EnsureUniqueFileName();
                    }
                }
                else
                {
                    forkPath = destFile;
                }
            }
            if (!string.IsNullOrEmpty(forkPath) && !System.IO.File.Exists(forkPath))
            {
                retrievalNecessary = true;
                destinationDir     = System.IO.Path.GetDirectoryName(forkPath);
            }

            return(forkPath);
        }
 /// <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);
 }
 /// <summary>
 /// Initializes a new instance of DeleteOperationData.
 /// </summary>
 /// <param name="taskData">Task data for error reporting.</param>
 /// <param name="targetType">The entry type being deleted.</param>
 public DeleteOperationData(ExecuteDeviceCommandAsyncTaskData taskData, LfsEntityType targetType)
     : base(LfsOperations.Remove, taskData)
 {
     TargetType = targetType;
 }
 public UploadDataOperationData(ExecuteDeviceCommandAsyncTaskData taskData, uint address)
     : base(LfsOperations.None, taskData)
 {
     Address = address;
 }