/// <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())); }
/// <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 }
/// <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)); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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)); }
/// <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; } }
/// <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)); }
/// <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; } }