Пример #1
0
        /// <summary>
        /// Gets the patch infos for each <see cref="Patch"/>
        /// </summary>
        /// <param name="ipkStream">The IPK file stream</param>
        /// <returns>The patch infos</returns>
        protected IEnumerable <PatchInfo> GetPatchInfos(Stream ipkStream)
        {
            // Deserialize the IPK file
            var ipk = BinarySerializableHelpers.ReadFromStream <UbiArtIpkData>(ipkStream, UbiArtSettings.GetDefaultSettings(UbiArtGame.RaymanLegends, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Enumerate every patch
            foreach (var patchGroup in GetPatches.GroupBy(x => x.FileName))
            {
                // Get the file
                var file = ipk.Files.FindItem(x => x.Path.FileName == patchGroup.Key);

                // Make sure we found the file
                if (file == null)
                {
                    throw new Exception("Patch file not found");
                }

                // Make sure it's not compressed
                if (file.IsCompressed)
                {
                    throw new Exception("The configuration file is compressed and can not be edited");
                }

                // Get the offsets
                foreach (Patch patch in patchGroup)
                {
                    // Get the offset of the byte in the file to change
                    yield return(new PatchInfo(patch, (long)(ipk.BaseOffset + file.Offsets.First() + (uint)patch.FileOffset)));
                }
            }
        }
        /// <summary>
        /// Gets the .tex file data
        /// </summary>
        /// <param name="bytes">The bytes to get the data from</param>
        /// <returns>The .tex file data</returns>
        protected UbiArtTEXFile GetTexFile(byte[] bytes)
        {
            // Create a memory stream
            using var memoryStream = new MemoryStream(bytes);

            return(BinarySerializableHelpers.ReadFromStream <UbiArtTEXFile>(memoryStream, Settings, RCPServices.App.GetBinarySerializerLogger()));
        }
Пример #3
0
        /// <summary>
        /// Updates the archive with the modified files
        /// </summary>
        /// <param name="archive">The loaded archive data</param>
        /// <param name="outputFileStream">The file stream for the updated archive</param>
        /// <param name="files">The import data for the archive files</param>
        public void UpdateArchive(object archive, Stream outputFileStream, IEnumerable <IArchiveImportData> files)
        {
            RL.Logger?.LogInformationSource($"An IPK archive is being repacked...");

            // Get the archive data
            var data = archive.CastTo <UbiArtIpkData>();

            // Create the file generator
            using var fileGenerator = new ArchiveFileGenerator <UbiArtIPKFileEntry>();

            // Keep track of the current pointer position
            ulong currentOffset = 0;

            // Handle each file
            foreach (var importData in files)
            {
                // Get the file
                var file = importData.FileEntryData.CastTo <UbiArtIPKFileEntry>();

                // Reset the offset array to always contain 1 item
                file.Offsets = new ulong[]
                {
                    file.Offsets?.FirstOrDefault() ?? 0
                };

                // Set the count
                file.OffsetCount = (uint)file.Offsets.Length;

                // Add to the generator
                fileGenerator.Add(file, () =>
                {
                    // Get the file bytes to write to the archive
                    var bytes = importData.GetData(file);

                    // Set the offset
                    file.Offsets[0] = currentOffset;

                    // Increase by the file size
                    currentOffset += file.ArchiveSize;

                    return(bytes);
                });
            }

            // Set the base offset
            data.BaseOffset = data.GetHeaderSize(Settings);

            // Write the files
            data.WriteArchiveContent(outputFileStream, fileGenerator, Config.ShouldCompress(data));

            outputFileStream.Position = 0;

            // Serialize the data
            BinarySerializableHelpers.WriteToStream(data, outputFileStream, Settings, RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"The IPK archive has been repacked");
        }
        /// <summary>
        /// Updates the archive with the modified files
        /// </summary>
        /// <param name="archive">The loaded archive data</param>
        /// <param name="outputFileStream">The file stream for the updated archive</param>
        /// <param name="files">The import data for the archive files</param>
        public void UpdateArchive(object archive, Stream outputFileStream, IEnumerable <IArchiveImportData> files)
        {
            RL.Logger?.LogInformationSource($"A CNT archive is being repacked...");

            // Get the archive data
            var data = archive.CastTo <OpenSpaceCntData>();

            // Create the file generator
            using var fileGenerator = new ArchiveFileGenerator <OpenSpaceCntFileEntry>();

            // Set the current pointer position to the header size
            var pointer = data.GetHeaderSize(Settings);

            // Disable checksum
            data.IsChecksumUsed = false;
            data.DirChecksum    = 0;

            // NOTE: We can't disable the XOR key entirely as that would disable it for the file bytes too, which would require them all to be decrypted
            // Reset XOR keys
            data.XORKey = 0;

            // Load each file
            foreach (var importData in files)
            {
                // Get the file entry
                var file = importData.FileEntryData.CastTo <OpenSpaceCntFileEntry>();

                // Reset checksum and XOR key
                file.Checksum = 0;
                file.XORKey   = 0;

                // Add to the generator
                fileGenerator.Add(file, () =>
                {
                    // Get the file bytes to write to the archive
                    var bytes = importData.GetData(file);

                    // Set the pointer
                    file.Pointer = pointer;

                    // Update the pointer by the file size
                    pointer += file.Size;

                    return(bytes);
                });
            }

            // Write the files
            data.WriteArchiveContent(outputFileStream, fileGenerator);

            outputFileStream.Position = 0;

            // Serialize the data
            BinarySerializableHelpers.WriteToStream(data, outputFileStream, Settings, RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"The CNT archive has been repacked");
        }
        /// <summary>
        /// Converts the import file data from the input stream to the output stream
        /// </summary>
        /// <param name="fileBytes">The file bytes</param>
        /// <param name="inputStream">The input stream to import from</param>
        /// <param name="outputStream">The destination stream</param>
        /// <param name="format">The file format to use</param>
        public void ConvertImportData(byte[] fileBytes, Stream inputStream, Stream outputStream, FileExtension format)
        {
            // Load the bitmap
            using var bmp = new Bitmap(inputStream);

            // Load the current file
            OpenSpaceGFFile gf = GetFileContent(fileBytes);

            // IDEA: If bmp is not in supported format, then convert it?

            RawBitmapData rawBitmapData;

            // Get the bitmap lock
            using (var bmpLock = new BitmapLock(bmp))
            {
                // Get the raw bitmap data
                rawBitmapData = new RawBitmapData(bmp.Width, bmp.Height, bmpLock.Pixels, bmp.PixelFormat);

                // Force the new pixel format to be 888 or 8888 if set to do so
                if (RCPServices.Data.Archive_GF_ForceGF8888Import)
                {
                    gf.GFPixelFormat = gf.GFPixelFormat.SupportsTransparency() ? OpenSpaceGFFormat.Format_32bpp_BGRA_8888 : OpenSpaceGFFormat.Format_24bpp_BGR_888;
                }

                // Check if the format should be updated for transparency
                if (RCPServices.Data.Archive_GF_UpdateTransparency != Archive_GF_TransparencyMode.PreserveFormat)
                {
                    // NOTE: Only 24 and 32 bpp bitmaps are supported
                    // Check if the imported file is transparent
                    var isTransparent = bmp.PixelFormat switch
                    {
                        PixelFormat.Format32bppArgb => (RCPServices.Data.Archive_GF_UpdateTransparency == Archive_GF_TransparencyMode.UpdateBasedOnPixelFormat ||
                                                        bmpLock.UtilizesAlpha()),
                        PixelFormat.Format24bppRgb => false,
                        _ => (bool?)null
                    };

                    // NOTE: Currently only supported for formats with 3 or 4 channels
                    // Check if the format should be updated for transparency
                    if (gf.Channels >= 3 && isTransparent != null)
                    {
                        // Update the format
                        gf.GFPixelFormat = isTransparent.Value ? OpenSpaceGFFormat.Format_32bpp_BGRA_8888 : OpenSpaceGFFormat.Format_24bpp_BGR_888;
                    }
                }
            }

            // Import the bitmap
            gf.ImportFromBitmap(Settings, rawBitmapData, RCPServices.Data.Archive_GF_GenerateMipmaps);

            // Serialize the data to get the bytes
            BinarySerializableHelpers.WriteToStream(gf, outputStream, Settings, RCPServices.App.GetBinarySerializerLogger());
        }

        #endregion
    }
Пример #6
0
        /// <summary>
        /// Get the progression slot view model for the save data from the specified file
        /// </summary>
        /// <param name="filePath">The slot file path</param>
        /// <returns>The progression slot view model</returns>
        protected ProgressionSlotViewModel GetProgressionSlotViewModel(FileSystemPath filePath)
        {
            RL.Logger?.LogInformationSource($"Rayman 3 slot {filePath.Name} is being loaded...");

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                return(null);
            }

            // Open the file in a stream
            using var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);

            // Create a memory stream
            using var memStream = new MemoryStream();

            // Decode the data
            new Rayman3SaveDataEncoder().Decode(fileStream, memStream);

            // Set the position
            memStream.Position = 0;

            // Deserialize the data
            var saveData = BinarySerializableHelpers.ReadFromStream <Rayman3PCSaveData>(memStream, OpenSpaceSettings.GetDefaultSettings(OpenSpaceGame.Rayman3, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Slot has been deserialized");

            var formatInfo = new NumberFormatInfo()
            {
                NumberGroupSeparator = " ",
                NumberDecimalDigits  = 0
            };

            // Create the collection with items for each level + general information
            var progressItems = new ProgressionInfoItemViewModel[]
            {
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Cage, new LocalizedString(() => $"{saveData.TotalCages}/60")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_TotalHeader}: {saveData.TotalScore.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level1Header}: {saveData.Levels[0].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level2Header}: {saveData.Levels[1].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level3Header}: {saveData.Levels[2].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level4Header}: {saveData.Levels[3].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level5Header}: {saveData.Levels[4].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level6Header}: {saveData.Levels[5].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level7Header}: {saveData.Levels[6].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level8Header}: {saveData.Levels[7].Score.ToString("n", formatInfo)}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R3_Score, new LocalizedString(() => $"{Resources.Progression_R3_Level9Header}: {saveData.Levels[8].Score.ToString("n", formatInfo)}"))
            };

            RL.Logger?.LogInformationSource($"General progress info has been set");

            // Return the data with the collection
            return(new Rayman3ProgressionSlotViewModel(new LocalizedString(() => $"{filePath.RemoveFileExtension().Name}"), progressItems, filePath, this));
        }
Пример #7
0
        /// <summary>
        /// Exports the save slot from the specified path
        /// </summary>
        /// <param name="outputFilePath">The output file path</param>
        /// <returns>The task</returns>
        protected override Task ExportSaveDataAsync(FileSystemPath outputFilePath)
        {
            // Get the serialized data
            var data = BinarySerializableHelpers.ReadFromFile <RaymanMPCSaveData>(SaveSlotFilePath, OpenSpaceSettings.GetDefaultSettings(OpenSpaceGame.RaymanM, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Export the data
            JsonHelpers.SerializeToFile(data, outputFilePath);

            return(Task.CompletedTask);
        }
        /// <summary>
        /// Exports the save slot from the specified path
        /// </summary>
        /// <param name="outputFilePath">The output file path</param>
        /// <returns>The task</returns>
        protected override Task ExportSaveDataAsync(FileSystemPath outputFilePath)
        {
            // Get the serialized level data
            var data = BinarySerializableHelpers.ReadFromFile <JungleRunPCSaveData>(SaveSlotFilePath, UbiArtSettings.GetSaveSettings(UbiArtGame.RaymanJungleRun, Platform.PC), RCPServices.App.GetBinarySerializerLogger()).Levels;

            // Export the data
            JsonHelpers.SerializeToFile(data, outputFilePath);

            return(Task.CompletedTask);
        }
Пример #9
0
        /// <summary>
        /// Imports an exported save slot to the save slot from the specified path
        /// </summary>
        /// <param name="inputFilePath">The input file path</param>
        /// <returns>The task</returns>
        protected override Task ImportSaveDataAsync(FileSystemPath inputFilePath)
        {
            // Deserialize the input data
            var data = JsonHelpers.DeserializeFromFile <RaymanMPCSaveData>(inputFilePath);

            // Import the data
            BinarySerializableHelpers.WriteToFile(data, SaveSlotFilePath, OpenSpaceSettings.GetDefaultSettings(OpenSpaceGame.RaymanM, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            return(Task.CompletedTask);
        }
Пример #10
0
        /// <summary>
        /// Gets the size of the header information in the archive
        /// </summary>
        /// <param name="settings">The serializer settings</param>
        /// <returns>The size in bytes</returns>
        public uint GetHeaderSize(Ray1Settings settings)
        {
            // Create a temporary memory stream to determine the size
            using var stream = new MemoryStream();

            // Serialize the header only
            BinarySerializableHelpers.WriteToStream(this, stream, settings);

            // Get the position, which will be the size of the header
            return((uint)stream.Position);
        }
        /// <summary>
        /// Serializes the data to the localization file
        /// </summary>
        /// <param name="file">The localization file</param>
        /// <param name="data">The data</param>
        protected override void Serialize(FileSystemPath file, UbiArtLocStringValuePair[] data)
        {
            // Read the current data to get the remaining bytes
            var currentData = BinarySerializableHelpers.ReadFromFile <UbiArtLocalizationData>(file, UbiArtSettings.GetDefaultSettings(UbiArtGame.RaymanOrigins, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Replace the string data
            currentData.Strings = data;

            // Serialize the data
            BinarySerializableHelpers.WriteToFile(currentData, file, UbiArtSettings.GetDefaultSettings(UbiArtGame.RaymanOrigins, Platform.PC), RCPServices.App.GetBinarySerializerLogger());
        }
        /// <summary>
        /// Loads the archive
        /// </summary>
        /// <param name="archiveFileStream">The file stream for the archive</param>
        /// <returns>The archive data</returns>
        public object LoadArchive(Stream archiveFileStream)
        {
            // Set the stream position to 0
            archiveFileStream.Position = 0;

            // Load the current file
            var data = BinarySerializableHelpers.ReadFromStream <UbiArtIpkData>(archiveFileStream, Settings, RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Read IPK file ({data.Version}) with {data.FilesCount} files");

            return(data);
        }
        /// <summary>
        /// Loads the archive
        /// </summary>
        /// <param name="archiveFileStream">The file stream for the archive</param>
        /// <returns>The archive data</returns>
        public object LoadArchive(Stream archiveFileStream)
        {
            // Set the stream position to 0
            archiveFileStream.Position = 0;

            // Load the current file
            var data = BinarySerializableHelpers.ReadFromStream <OpenSpaceCntData>(archiveFileStream, Settings, RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Read CNT file with {data.Files.Length} files and {data.Directories.Length} directories");

            return(data);
        }
        /// <summary>
        /// Imports an exported save slot to the save slot from the specified path
        /// </summary>
        /// <param name="inputFilePath">The input file path</param>
        /// <returns>The task</returns>
        protected override Task ImportSaveDataAsync(FileSystemPath inputFilePath)
        {
            // Get the serialized data
            var data = BinarySerializableHelpers.ReadFromFile <JungleRunPCSaveData>(SaveSlotFilePath, UbiArtSettings.GetSaveSettings(UbiArtGame.RaymanJungleRun, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Deserialize the input data
            data.Levels = JsonHelpers.DeserializeFromFile <JungleRunPCSaveDataLevel[]>(inputFilePath);

            // Import the data
            BinarySerializableHelpers.WriteToFile(data, SaveSlotFilePath, UbiArtSettings.GetSaveSettings(UbiArtGame.RaymanJungleRun, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            return(Task.CompletedTask);
        }
        /// <summary>
        /// Get the progression slot view model for the save data from the specified file
        /// </summary>
        /// <param name="filePath">The slot file path</param>
        /// <returns>The progression slot view model</returns>
        protected ProgressionSlotViewModel GetProgressionSlotViewModel(FileSystemPath filePath)
        {
            RL.Logger?.LogInformationSource($"Rayman 1 slot {filePath.Name} is being loaded...");

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                return(null);
            }

            // Open the file in a stream
            using var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);

            // Create a memory stream
            using var memStream = new MemoryStream();

            // Decode the data
            new Rayman12PCSaveDataEncoder().Decode(fileStream, memStream);

            // Set the position
            memStream.Position = 0;

            // Deserialize and return the data
            var saveData = BinarySerializableHelpers.ReadFromStream <Rayman1PCSaveData>(memStream, Ray1Settings.GetDefaultSettings(Ray1Game.Rayman1, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Slot has been deserialized");

            // Get total amount of cages
            var cages = saveData.Wi_Save_Zone.Sum(x => x.Cages);

            // Create the collection with items for cages + lives
            var progressItems = new ProgressionInfoItemViewModel[]
            {
                new ProgressionInfoItemViewModel(ProgressionIcons.R1_Cage, new LocalizedString(() => $"{cages}/102")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R1_Continue, new LocalizedString(() => $"{saveData.ContinuesCount}")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R1_Life, new LocalizedString(() => $"{saveData.StatusBar.LivesCount}")),
            };

            RL.Logger?.LogInformationSource($"General progress info has been set");

            // Calculate the percentage
            var percentage = ((cages / 102d * 100)).ToString("0.##");

            RL.Logger?.LogInformationSource($"Slot percentage is {percentage}%");

            // Return the data with the collection
            return(new Rayman1ProgressionSlotViewModel(new LocalizedString(() => $"{saveData.SaveName.ToUpper()} ({percentage}%)"), progressItems, filePath, this));
        }
        /// <summary>
        /// Gets the contents of the file with an option to deserialize mipmaps
        /// </summary>
        /// <param name="fileBytes">The file bytes</param>
        /// <returns>The deserialized file</returns>
        public OpenSpaceGFFile GetFileContent(byte[] fileBytes)
        {
            // Load the bytes into a memory stream
            using var stream = new MemoryStream(fileBytes);

            // Serialize the data
            var data = BinarySerializableHelpers.ReadFromStream <OpenSpaceGFFile>(stream, Settings, RCPServices.App.GetBinarySerializerLogger());

            // Make sure we read the entire file
            if (stream.Position != stream.Length)
            {
                RL.Logger?.LogWarningSource($"The GF file {FileName} was not fully read");
            }

            // Return the data
            return(data);
        }
Пример #17
0
        /// <summary>
        /// Get the progression slot view models for the save data from the specified file
        /// </summary>
        /// <param name="filePath">The slot file path</param>
        /// <returns>The progression slot view models</returns>
        protected IEnumerable <ProgressionSlotViewModel> GetProgressionSlotViewModels(FileSystemPath filePath)
        {
            RL.Logger?.LogInformationSource($"Rayman M/Arena save file {filePath.Name} is being loaded...");

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                yield break;
            }

            // Deserialize the save data
            var saveData = BinarySerializableHelpers.ReadFromFile <RaymanMPCSaveData>(filePath, OpenSpaceSettings.GetDefaultSettings(OpenSpaceGame.RaymanM, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Save file has been deserialized");

            // Helper for getting the entry with a specific key
            int[] GetValues(string key) => saveData.Items.First(x => x.Key == key).Values;
        /// <summary>
        /// Imports an exported save slot to the save slot from the specified path
        /// </summary>
        /// <param name="inputFilePath">The input file path</param>
        /// <returns>The task</returns>
        protected override Task ImportSaveDataAsync(FileSystemPath inputFilePath)
        {
            // Deserialize the input data
            var data = JsonHelpers.DeserializeFromFile <Rayman1PCSaveData>(inputFilePath);

            // Create streams
            using var decodedDataStream = new MemoryStream();
            using var saveFileStream    = File.Create(SaveSlotFilePath);

            // Import the data
            BinarySerializableHelpers.WriteToStream(data, decodedDataStream, Ray1Settings.GetDefaultSettings(Ray1Game.Rayman1, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Set position to 0
            decodedDataStream.Position = 0;

            // Encode the data to the file
            new Rayman12PCSaveDataEncoder().Encode(decodedDataStream, saveFileStream);

            return(Task.CompletedTask);
        }
        /// <summary>
        /// Exports the save slot from the specified path
        /// </summary>
        /// <param name="outputFilePath">The output file path</param>
        /// <returns>The task</returns>
        protected override Task ExportSaveDataAsync(FileSystemPath outputFilePath)
        {
            // Create streams
            using var saveFileStream    = File.OpenRead(SaveSlotFilePath);
            using var decodedDataStream = new MemoryStream();

            // Decode the save file
            new Rayman12PCSaveDataEncoder().Decode(saveFileStream, decodedDataStream);

            // Set position to 0
            decodedDataStream.Position = 0;

            // Get the serialized data
            var data = BinarySerializableHelpers.ReadFromStream <Rayman1PCSaveData>(decodedDataStream, Ray1Settings.GetDefaultSettings(Ray1Game.Rayman1, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Export the data
            JsonHelpers.SerializeToFile(data, outputFilePath);

            return(Task.CompletedTask);
        }
Пример #20
0
        /// <summary>
        /// Loads the current save data if available
        /// </summary>
        protected override void LoadData()
        {
            // Make sure the file exists
            if (!ConfigFilePath.FileExists)
            {
                return;
            }

            // Create streams
            using var saveFileStream    = File.OpenRead(ConfigFilePath);
            using var decodedDataStream = new MemoryStream();

            // Decode the save file
            new Rayman12PCSaveDataEncoder().Decode(saveFileStream, decodedDataStream);

            // Set position to 0
            decodedDataStream.Position = 0;

            // Get the serialized data
            var config = BinarySerializableHelpers.ReadFromStream <Rayman2PCConfigData>(decodedDataStream, OpenSpaceSettings.GetDefaultSettings(OpenSpaceGame.Rayman2, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            // Read and set slot data
            ProgressionSlots.AddRange(config.Slots.Select(x => GetProgressionSlotViewModel(SaveGamePath + $"Slot{x.SlotIndex}" + "General.sav", x.SlotDisplayName)));
        }
 /// <summary>
 /// Deserializes the localization file
 /// </summary>
 /// <param name="file">The localization file</param>
 /// <returns>The data</returns>
 protected override UbiArtLocStringValuePair[] Deserialize(FileSystemPath file)
 {
     return(BinarySerializableHelpers.ReadFromFile <UbiArtLocalizationData>(file, UbiArtSettings.GetDefaultSettings(UbiArtGame.RaymanOrigins, Platform.PC), RCPServices.App.GetBinarySerializerLogger()).Strings);
 }
Пример #22
0
        /// <summary>
        /// Get the progression slot view model for the save data from the specified file
        /// </summary>
        /// <param name="filePath">The slot file path</param>
        /// <returns>The progression slot view model</returns>
        protected ProgressionSlotViewModel GetProgressionSlotViewModel(FileSystemPath filePath)
        {
            RL.Logger?.LogInformationSource($"Legends slot {filePath.Parent.Name} is being loaded...");

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                return(null);
            }

            // Deserialize and return the data
            var saveData = BinarySerializableHelpers.ReadFromFile <LegendsPCSaveData>(filePath, UbiArtSettings.GetSaveSettings(UbiArtGame.RaymanLegends, Platform.PC), RCPServices.App.GetBinarySerializerLogger()).SaveData;

            RL.Logger?.LogInformationSource($"Slot has been deserialized");

            // Create the collection with items for each time trial level + general information
            var progressItems = new List <ProgressionInfoItemViewModel>();

            // Create the number format info to use
            var formatInfo = new NumberFormatInfo()
            {
                NumberGroupSeparator = " ",
                NumberDecimalDigits  = 0
            };

            // Get the total amount of freed teensies
            var teensies = saveData.Levels.Select(x => x.Value.Object.FreedPrisoners.Length).Sum() + saveData.LuckyTicketRewardList.Count(x => x.Type == 5);

            // Set general progress info
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RL_Teensy, new LocalizedString(() => $"{teensies}/700")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RL_Lum, new LocalizedString(() => $"{saveData.Score.LocalLumsCount.ToString("n", formatInfo)}")));

            // Set rank
            progressItems.Add(new ProgressionInfoItemViewModel(Enum.Parse(typeof(ProgressionIcons), $"RL_Rank{saveData.Profile.StatusIcon}").CastTo <ProgressionIcons>(), new LocalizedString(() => $"{saveData.Profile.StatusIcon}")));

            // Set cups
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RL_Bronze, new LocalizedString(() => $"{saveData.Profile.BronzeMedals.ToString("n", formatInfo)}")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RL_Silver, new LocalizedString(() => $"{saveData.Profile.SilverMedals.ToString("n", formatInfo)}")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RL_Gold, new LocalizedString(() => $"{saveData.Profile.GoldMedals.ToString("n", formatInfo)}")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RL_Diamond, new LocalizedString(() => $"{saveData.Profile.DiamondMedals.ToString("n", formatInfo)}")));

            RL.Logger?.LogInformationSource($"General progress info has been set");

            // Get the level IDs
            var lvlIds = GetLevelIDs;

            // Set invasion times
            progressItems.AddRange(saveData.Levels.
                                   Select(x => x.Value.Object).
                                   Where(x => x.BestTime > 0).
                                   Select(x => (lvlIds[x.Id.ID], x.BestTime)).
                                   Select(x => new ProgressionInfoItemViewModel(
                                              Enum.Parse(typeof(ProgressionIcons), $"RL_Inv_{x.Item1.Replace("-", "_")}").CastTo <ProgressionIcons>(),
                                              new LocalizedString(() => $"{x.Item1}: {x.BestTime:0.000}"),
                                              new LocalizedString(() => Resources.ResourceManager.GetString($"RL_LevelName_{x.Item1.Replace("-", "_")}")))).
                                   OrderBy(x => x.Content.Value));

            RL.Logger?.LogInformationSource($"Invasion progress info has been set");

            // Calculate the percentage
            var percentage = ((teensies / 700d * 100)).ToString("0.##");

            RL.Logger?.LogInformationSource($"Slot percentage is {percentage}%");

            // Return the data with the collection
            return(new LegendsProgressionSlotViewModel(new LocalizedString(() => $"{saveData.Profile.Name} ({percentage}%)"), progressItems.ToArray(), filePath, this));
        }
Пример #23
0
        /// <summary>
        /// Get the progression slot view model for the save data from the specified file
        /// </summary>
        /// <param name="filePath">The slot file path</param>
        /// <param name="slotName">The generator for the name of the save slot</param>
        /// <returns>The progression slot view model</returns>
        protected ProgressionSlotViewModel GetProgressionSlotViewModel(FileSystemPath filePath, string slotName)
        {
            RL.Logger?.LogInformationSource($"Rayman 2 slot {slotName} is being loaded...");

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                return(null);
            }

            // Open the file in a stream
            using var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);

            // Create a memory stream
            using var memStream = new MemoryStream();

            // Decode the data
            new Rayman12PCSaveDataEncoder().Decode(fileStream, memStream);

            // Set the position
            memStream.Position = 0;

            // Deserialize and return the data
            var saveData = BinarySerializableHelpers.ReadFromStream <Rayman2PCSaveData>(memStream, OpenSpaceSettings.GetDefaultSettings(OpenSpaceGame.Rayman2, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Slot has been deserialized");

            // Get the bit array
            var array = saveData.GlobalArrayAsBitFlags();

            // Get total amount of Lums and cages
            var lums =
                array.Skip(0).Take(800).Select(x => x ? 1 : 0).Sum() +
                array.Skip(1200).Take(194).Select(x => x ? 1 : 0).Sum() +
                // Woods of Light
                array.Skip(1395).Take(5).Select(x => x ? 1 : 0).Sum() +
                // 1000th Lum
                (array[1013] ? 1 : 0);

            var cages           = array.Skip(839).Take(80).Select(x => x ? 1 : 0).Sum();
            var walkOfLifeTime  = saveData.GlobalArray[12] * 10;
            var walkOfPowerTime = saveData.GlobalArray[11] * 10;

            // Create the collection with items for cages + lives
            var progressItems = new List <ProgressionInfoItemViewModel>
            {
                new ProgressionInfoItemViewModel(ProgressionIcons.R2_Lum, new LocalizedString(() => $"{lums}/1000")),
                new ProgressionInfoItemViewModel(ProgressionIcons.R2_Cage, new LocalizedString(() => $"{cages}/80")),
            };

            if (walkOfLifeTime > 120)
            {
                progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.R2_Clock, new LocalizedString(() => $"{new TimeSpan(0, 0, 0, 0, walkOfLifeTime):mm\\:ss\\:ff}"), new LocalizedString(() => Resources.R2_BonusLevelName_1)));
            }

            if (walkOfPowerTime > 120)
            {
                progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.R2_Clock, new LocalizedString(() => $"{new TimeSpan(0, 0, 0, 0, walkOfPowerTime):mm\\:ss\\:ff}"), new LocalizedString(() => Resources.R2_BonusLevelName_2)));
            }

            RL.Logger?.LogInformationSource($"General progress info has been set");

            // Get the name and percentage
            var separatorIndex = slotName.LastIndexOf((char)0x20);
            var name           = slotName.Substring(0, separatorIndex);
            var percentage     = slotName.Substring(separatorIndex + 1);

            RL.Logger?.LogInformationSource($"Slot percentage is {percentage}%");

            // Return the data with the collection
            return(new Rayman2ProgressionSlotViewModel(new LocalizedString(() => $"{name} ({percentage}%)"), progressItems.ToArray(), filePath, this));
        }
        /// <summary>
        /// Converts files using the specified serializer and convert action
        /// </summary>
        /// <typeparam name="T">The type of data to convert</typeparam>
        /// <param name="settings">The serializer settings</param>
        /// <param name="convertAction">The convert action, converting the data to the specified file path</param>
        /// <param name="fileFilter">The file filter when selecting files to convert</param>
        /// <param name="supportedFileExtensions">The supported file extensions to export as</param>
        /// <param name="defaultDir">The default directory</param>
        /// <param name="encoder">An optional data encoder to use</param>
        /// <returns>The task</returns>
        protected async Task ConvertFromAsync <T>(IBinarySerializerSettings settings, Action <T, FileSystemPath> convertAction, string fileFilter, string[] supportedFileExtensions, FileSystemPath?defaultDir, IDataEncoder encoder = null)
            where T : IBinarySerializable, new()
        {
            if (IsLoading)
            {
                return;
            }

            try
            {
                IsLoading = true;

                // Make sure the directory exists
                if (defaultDir?.DirectoryExists != true)
                {
                    defaultDir = null;
                }

                // Allow the user to select the files
                var fileResult = await Services.BrowseUI.BrowseFileAsync(new FileBrowserViewModel()
                {
                    Title            = Resources.Utilities_Converter_FileSelectionHeader,
                    DefaultDirectory = defaultDir?.FullPath,
                    ExtensionFilter  = fileFilter,
                    MultiSelection   = true
                });

                if (fileResult.CanceledByUser)
                {
                    return;
                }

                // Allow the user to select the destination directory
                var destinationResult = await Services.BrowseUI.BrowseDirectoryAsync(new DirectoryBrowserViewModel()
                {
                    Title = Resources.Browse_DestinationHeader,
                });

                if (destinationResult.CanceledByUser)
                {
                    return;
                }

                // Allow the user to select the file extension to export as
                var extResult = await RCPServices.UI.SelectFileExtensionAsync(new FileExtensionSelectionDialogViewModel(supportedFileExtensions, Resources.Utilities_Converter_ExportExtensionHeader));

                if (extResult.CanceledByUser)
                {
                    return;
                }

                try
                {
                    await Task.Run(() =>
                    {
                        // Convert every file
                        foreach (var file in fileResult.SelectedFiles)
                        {
                            Stream stream = null;

                            try
                            {
                                if (encoder != null)
                                {
                                    // Open the file in a stream
                                    using var fileStream = File.Open(file, FileMode.Open, FileAccess.Read);

                                    // Create a memory stream
                                    stream = new MemoryStream();

                                    // Decode the data
                                    encoder.Decode(fileStream, stream);

                                    // Set the position
                                    stream.Position = 0;
                                }
                                else
                                {
                                    stream = File.Open(file, FileMode.Open, FileAccess.Read);
                                }

                                // Read the file data
                                var data = BinarySerializableHelpers.ReadFromStream <T>(stream, settings, RCPServices.App.GetBinarySerializerLogger());

                                // Get the destination file
                                var destinationFile = destinationResult.SelectedDirectory + file.Name;

                                // Set the file extension
                                destinationFile = destinationFile.ChangeFileExtension(new FileExtension(extResult.SelectedFileFormat)).GetNonExistingFileName();

                                // Convert the file
                                convertAction(data, destinationFile);
                            }
                            finally
                            {
                                stream?.Dispose();
                            }
                        }
                    });

                    await Services.MessageUI.DisplaySuccessfulActionMessageAsync(Resources.Utilities_Converter_Success);
                }
                catch (Exception ex)
                {
                    ex.HandleError("Converting files");

                    await Services.MessageUI.DisplayExceptionMessageAsync(ex, Resources.Utilities_Converter_Error);
                }
            }
            finally
            {
                IsLoading = false;
            }
        }
Пример #25
0
        /// <summary>
        /// Get the progression slot view model for the save data from the specified file
        /// </summary>
        /// <param name="filePath">The slot file path</param>
        /// <param name="slotNamegenerator">The generator for the name of the save slot</param>
        /// <returns>The progression slot view model</returns>
        protected ProgressionSlotViewModel GetProgressionSlotViewModel(FileSystemPath filePath, Func <string> slotNamegenerator)
        {
            RL.Logger?.LogInformationSource($"Origins slot {filePath.Name} is being loaded...");

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                return(null);
            }

            // Deserialize and get the data
            var saveData = BinarySerializableHelpers.ReadFromFile <OriginsPCSaveData>(filePath, UbiArtSettings.GetSaveSettings(UbiArtGame.RaymanOrigins, Platform.PC), RCPServices.App.GetBinarySerializerLogger()).SaveData;

            RL.Logger?.LogInformationSource($"Slot has been deserialized");

            // Get the level configuration
            var lvlConfig = JsonConvert.DeserializeObject <ROLevelConfig>(Files.RO_LevelConfig);

            int completed   = 0;
            int cageMaps    = 0;
            int lumAttack1  = 0;
            int lumAttack2  = 0;
            int lumAttack3  = 0;
            int timeAttack1 = 0;
            int timeAttack2 = 0;

            // Get number of levels where each mission has been completed
            foreach (var lvl in saveData.Levels)
            {
                // Get the configuration for the level
                var level   = lvlConfig.GetLevel(lvl.Key.ID);
                var mission = lvlConfig.GetMission(lvl.Key.ID);

                // Make sure it's a normal level, i.e. it has a medallion
                if (level == null || mission == null)
                {
                    continue;
                }

                // Check if the level has been completed
                if (lvl.Value.Object.LevelState == OriginsPCSaveData.SPOT_STATE.COMPLETED)
                {
                    completed++;
                }

                // Get the number of completed cage maps (between 0-2)
                cageMaps += lvl.Value.Object.CageMapPassedDoors.Length;

                // Get the best time attack score
                var timeAttack = lvl.Value.Object.BestTimeAttack;

                // Compare the time attack score with the targets
                if (timeAttack <= level.Time1)
                {
                    timeAttack1++;
                }
                if (timeAttack <= level.Time2)
                {
                    timeAttack2++;
                }

                // Get the best lum attack score
                var lumAttack = lvl.Value.Object.BestLumAttack;

                // Compare the lum attack score with the targets
                if (lumAttack >= mission.LumAttack1)
                {
                    lumAttack1++;
                }
                if (lumAttack >= mission.LumAttack2)
                {
                    lumAttack2++;
                }
                if (lumAttack >= mission.LumAttack3)
                {
                    lumAttack3++;
                }
            }

            // Create the collection with items for each time trial level + general information
            var progressItems = new List <ProgressionInfoItemViewModel>();

            // Get the number of Electoons
            var electoons =
                // Cages
                cageMaps +
                // Levels completed
                completed +
                // Lum attack 1
                lumAttack1 +
                // Lum attack 2
                lumAttack2 +
                // Time attack 1
                timeAttack1;
            var teeth = saveData.Levels.Select(x => x.Value.Object.ISDs.Select(y => y.Value.Object.TakenTooth.Length)).SelectMany(x => x).Sum();

            // Set general progress info
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RO_Electoon, new LocalizedString(() => $"{electoons}/246")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RO_RedTooth, new LocalizedString(() => $"{teeth}/10")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RO_Medal, new LocalizedString(() => $"{lumAttack3}/51")));
            progressItems.Add(new ProgressionInfoItemViewModel(ProgressionIcons.RO_Trophy, new LocalizedString(() => $"{timeAttack2}/31")));

            RL.Logger?.LogInformationSource($"General progress info has been set");

            // Calculate the percentage
            var percentage = ((electoons / 246d * 25) + (teeth / 10d * 25) + (lumAttack3 / 51d * 25) + (timeAttack2 / 31d * 25)).ToString("0.##");

            RL.Logger?.LogInformationSource($"Slot percentage is {percentage}%");

            // Return the data with the collection
            return(new OriginsProgressionSlotViewModel(new LocalizedString(() => $"{slotNamegenerator()} ({percentage}%)"), progressItems.ToArray(), filePath, this));
        }
Пример #26
0
        /// <summary>
        /// Get the progression slot view model for the save data from the specified file
        /// </summary>
        /// <param name="fileName">The slot file name, relative to the save directory</param>
        /// <param name="slotNamegenerator">The generator for the name of the save slot</param>
        /// <returns>The progression slot view model</returns>
        protected ProgressionSlotViewModel GetProgressionSlotViewModel(FileSystemPath fileName, Func <string> slotNamegenerator)
        {
            RL.Logger?.LogInformationSource($"Jungle Run slot {fileName.Name} is being loaded...");

            // Get the file path
            var filePath = SaveDir + fileName;

            // Make sure the file exists
            if (!filePath.FileExists)
            {
                RL.Logger?.LogInformationSource($"Slot was not loaded due to not being found");

                return(null);
            }

            // Deserialize and return the data
            var saveData = BinarySerializableHelpers.ReadFromFile <JungleRunPCSaveData>(filePath, UbiArtSettings.GetSaveSettings(UbiArtGame.RaymanJungleRun, Platform.PC), RCPServices.App.GetBinarySerializerLogger());

            RL.Logger?.LogInformationSource($"Slot has been deserialized");

            // Create the collection with items for each time trial level + general information
            var progressItems = new ProgressionInfoItemViewModel[(saveData.Levels.Length / 10) + 2];

            // Get data values
            int collectedLums  = 0;
            int availableLums  = 0;
            int collectedTeeth = 0;
            int availableTeeth = saveData.Levels.Length;

            RL.Logger?.LogTraceSource($"Levels are being enumerated...");

            // Enumerate each level
            for (int i = 0; i < saveData.Levels.Length; i++)
            {
                // Get the level data
                var levelData = saveData.Levels[i];

                // Check if the level is a normal level
                if ((i + 1) % 10 != 0)
                {
                    RL.Logger?.LogTraceSource($"Level index {i} is a normal level");

                    // Get the collected lums
                    collectedLums += levelData.LumsRecord;
                    availableLums += 100;

                    RL.Logger?.LogTraceSource($"{levelData.LumsRecord} Lums have been collected");

                    // Check if the level is 100% complete
                    if (levelData.LumsRecord >= 100)
                    {
                        collectedTeeth++;
                    }

                    continue;
                }

                RL.Logger?.LogTraceSource($"Level index {i} is a time trial level");

                // Make sure the level has been completed
                if (levelData.RecordTime == 0)
                {
                    RL.Logger?.LogTraceSource($"Level has not been completed");

                    continue;
                }

                RL.Logger?.LogTraceSource($"Level has been completed with the record time {levelData.RecordTime}");

                collectedTeeth++;

                // Get the level number, starting at 10
                var fullLevelNumber = (i + 11).ToString();

                // Get the world and level numbers
                var worldNum = fullLevelNumber[0].ToString();
                var lvlNum   = fullLevelNumber[1].ToString();

                // If the level is 0, correct the numbers to be level 10
                if (lvlNum == "0")
                {
                    worldNum = (Int32.Parse(worldNum) - 1).ToString();
                    lvlNum   = "10";
                }

                // Create the view model
                progressItems[((i + 1) / 10) - 1 + 2] = new ProgressionInfoItemViewModel(ProgressionIcons.RO_Clock, new LocalizedString(() => $"{worldNum}-{lvlNum}: {new TimeSpan(0, 0, 0, 0, (int)levelData.RecordTime):mm\\:ss\\:fff}"));
            }

            // Set general progress info
            progressItems[0] = new ProgressionInfoItemViewModel(ProgressionIcons.RO_Lum, new LocalizedString(() => $"{collectedLums}/{availableLums}"));
            progressItems[1] = new ProgressionInfoItemViewModel(ProgressionIcons.RO_RedTooth, new LocalizedString(() => $"{collectedTeeth}/{availableTeeth}"));

            RL.Logger?.LogInformationSource($"General progress info has been set");

            // Calculate the percentage
            var percentage = ((collectedLums / (double)availableLums * 50) + (collectedTeeth / (double)availableTeeth * 50)).ToString("0.##");

            RL.Logger?.LogInformationSource($"Slot percentage is {percentage}%");

            // Return the data with the collection
            return(new JungleRunProgressionSlotViewModel(new LocalizedString(() => $"{slotNamegenerator()} ({percentage}%)"), progressItems, filePath, this));
        }
        /// <summary>
        /// Converts files using the specified serializer and convert action
        /// </summary>
        /// <typeparam name="T">The type of data to convert</typeparam>
        /// <param name="settings">The serializer settings</param>
        /// <param name="convertAction">The convert action, converting the data from the specified file path with the selected output format</param>
        /// <param name="fileFilter">The file filter when selecting files to convert</param>
        /// <param name="fileExtension">The file extension to export as</param>
        /// <param name="outputFormats">The available output formats</param>
        /// <param name="encoder">An optional data encoder to use</param>
        /// <returns>The task</returns>
        protected async Task ConvertToAsync <T>(IBinarySerializerSettings settings, Func <FileSystemPath, string, T> convertAction, string fileFilter, FileExtension fileExtension, string[] outputFormats = null, IDataEncoder encoder = null)
            where T : IBinarySerializable, new()
        {
            if (IsLoading)
            {
                return;
            }

            try
            {
                IsLoading = true;

                // Allow the user to select the files
                var fileResult = await Services.BrowseUI.BrowseFileAsync(new FileBrowserViewModel()
                {
                    Title           = Resources.Utilities_Converter_FileSelectionHeader,
                    ExtensionFilter = fileFilter,
                    MultiSelection  = true
                });

                if (fileResult.CanceledByUser)
                {
                    return;
                }

                // Allow the user to select the destination directory
                var destinationResult = await Services.BrowseUI.BrowseDirectoryAsync(new DirectoryBrowserViewModel()
                {
                    Title = Resources.Browse_DestinationHeader,
                });

                if (destinationResult.CanceledByUser)
                {
                    return;
                }

                try
                {
                    // The output format
                    string outputFormat = null;

                    // Get the output format if available
                    if (outputFormats?.Any() == true)
                    {
                        // Get the format
                        var formatResult = await RCPServices.UI.SelectFileExtensionAsync(new FileExtensionSelectionDialogViewModel(outputFormats, Resources.Utilities_Converter_FormatSelectionHeader));

                        if (formatResult.CanceledByUser)
                        {
                            return;
                        }

                        outputFormat = formatResult.SelectedFileFormat;
                    }

                    await Task.Run(() =>
                    {
                        // Convert every file
                        foreach (var file in fileResult.SelectedFiles)
                        {
                            // Get the destination file
                            var destinationFile = destinationResult.SelectedDirectory + file.Name;

                            // Set the file extension
                            destinationFile = destinationFile.ChangeFileExtension(fileExtension).GetNonExistingFileName();

                            // Convert the file
                            var data = convertAction(file, outputFormat);

                            if (encoder == null)
                            {
                                // Create the destination file
                                using var destinationFileStream = File.Open(destinationFile, FileMode.Create, FileAccess.Write);

                                // Save the converted data
                                BinarySerializableHelpers.WriteToStream(data, destinationFileStream, settings, RCPServices.App.GetBinarySerializerLogger());
                            }
                            else
                            {
                                // Create a memory stream
                                using var encodingStream = new MemoryStream();

                                // Serialize the converted data to the memory stream
                                BinarySerializableHelpers.WriteToStream(data, encodingStream, settings, RCPServices.App.GetBinarySerializerLogger());

                                // Create the destination file
                                using var destinationFileStream = File.Open(destinationFile, FileMode.Create, FileAccess.Write);

                                encodingStream.Position = 0;

                                // Encode the data to the file
                                encoder.Encode(encodingStream, destinationFileStream);
                            }
                        }
                    });

                    await Services.MessageUI.DisplaySuccessfulActionMessageAsync(Resources.Utilities_Converter_Success);
                }
                catch (Exception ex)
                {
                    ex.HandleError("Converting files");

                    await Services.MessageUI.DisplayExceptionMessageAsync(ex, Resources.Utilities_Converter_Error);
                }
            }
            finally
            {
                IsLoading = false;
            }
        }