From ce967290ef4809e9cb23ea640d14beb6792b7f5b Mon Sep 17 00:00:00 2001 From: AsvalGTA Date: Mon, 18 Mar 2019 22:00:42 +0100 Subject: [PATCH] added challenges bundle viewer [WIP] --- FModel/FModel.csproj | 2 + FModel/PAKWindow.Designer.cs | 20 +-- FModel/PAKWindow.cs | 143 +++++++++++++++- FModel/PAKWindow.resx | 2 +- FModel/Parser/ChallengeBundleIdParser.cs | 157 ++++++++++++++++++ FModel/Parser/ItemsIDParser.cs | 6 +- FModel/Parser/QuestParser.cs | 199 +++++++++++++++++++++++ README.md | 7 +- 8 files changed, 519 insertions(+), 17 deletions(-) create mode 100644 FModel/Parser/ChallengeBundleIdParser.cs create mode 100644 FModel/Parser/QuestParser.cs diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index 81d4f03a..d4103fc2 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -106,7 +106,9 @@ PAKWindow.cs + + diff --git a/FModel/PAKWindow.Designer.cs b/FModel/PAKWindow.Designer.cs index 2be38bc2..90fe0ff5 100644 --- a/FModel/PAKWindow.Designer.cs +++ b/FModel/PAKWindow.Designer.cs @@ -94,12 +94,12 @@ this.LoadContext.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.optionsToolStripMenuItem}); this.LoadContext.Name = "LoadContext"; - this.LoadContext.Size = new System.Drawing.Size(128, 28); + this.LoadContext.Size = new System.Drawing.Size(117, 26); // // optionsToolStripMenuItem // this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; - this.optionsToolStripMenuItem.Size = new System.Drawing.Size(127, 24); + this.optionsToolStripMenuItem.Size = new System.Drawing.Size(116, 22); this.optionsToolStripMenuItem.Text = "Options"; this.optionsToolStripMenuItem.Click += new System.EventHandler(this.optionsToolStripMenuItem_Click); // @@ -209,13 +209,13 @@ this.ImageContext.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.OpenImageTS}); this.ImageContext.Name = "ImageContext"; - this.ImageContext.Size = new System.Drawing.Size(155, 28); + this.ImageContext.Size = new System.Drawing.Size(140, 26); // // OpenImageTS // this.OpenImageTS.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; this.OpenImageTS.Name = "OpenImageTS"; - this.OpenImageTS.Size = new System.Drawing.Size(154, 24); + this.OpenImageTS.Size = new System.Drawing.Size(139, 22); this.OpenImageTS.Text = "Open Image"; this.OpenImageTS.Click += new System.EventHandler(this.OpenImageTS_Click); // @@ -240,7 +240,7 @@ this.toolStripSeparator1, this.mergeGeneratedImagesToolStripMenuItem}); this.ExtractAsset.Name = "ExtractAsset"; - this.ExtractAsset.Size = new System.Drawing.Size(252, 82); + this.ExtractAsset.Size = new System.Drawing.Size(223, 76); // // LoadDataTS // @@ -250,7 +250,7 @@ this.LoadDataTS.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; this.LoadDataTS.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; this.LoadDataTS.Name = "LoadDataTS"; - this.LoadDataTS.Size = new System.Drawing.Size(251, 24); + this.LoadDataTS.Size = new System.Drawing.Size(222, 22); this.LoadDataTS.Text = "Load Data After Serialization"; // // SaveImageTS @@ -258,18 +258,18 @@ this.SaveImageTS.CheckOnClick = true; this.SaveImageTS.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; this.SaveImageTS.Name = "SaveImageTS"; - this.SaveImageTS.Size = new System.Drawing.Size(251, 24); + this.SaveImageTS.Size = new System.Drawing.Size(222, 22); this.SaveImageTS.Text = "Auto Save Generated Image"; // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(248, 6); + this.toolStripSeparator1.Size = new System.Drawing.Size(219, 6); // // mergeGeneratedImagesToolStripMenuItem // this.mergeGeneratedImagesToolStripMenuItem.Name = "mergeGeneratedImagesToolStripMenuItem"; - this.mergeGeneratedImagesToolStripMenuItem.Size = new System.Drawing.Size(251, 24); + this.mergeGeneratedImagesToolStripMenuItem.Size = new System.Drawing.Size(222, 22); this.mergeGeneratedImagesToolStripMenuItem.Text = "Merge Generated Images"; this.mergeGeneratedImagesToolStripMenuItem.Click += new System.EventHandler(this.mergeGeneratedImagesToolStripMenuItem_Click); // @@ -279,7 +279,7 @@ this.ConsoleRichTextBox.Location = new System.Drawing.Point(6, 374); this.ConsoleRichTextBox.Name = "ConsoleRichTextBox"; this.ConsoleRichTextBox.ReadOnly = true; - this.ConsoleRichTextBox.Size = new System.Drawing.Size(922, 229); + this.ConsoleRichTextBox.Size = new System.Drawing.Size(922, 255); this.ConsoleRichTextBox.TabIndex = 6; this.ConsoleRichTextBox.Text = ""; // diff --git a/FModel/PAKWindow.cs b/FModel/PAKWindow.cs index 4777af6d..6509092b 100644 --- a/FModel/PAKWindow.cs +++ b/FModel/PAKWindow.cs @@ -1,4 +1,7 @@ -using Newtonsoft.Json; +using FModel.Items; +using FModel.Challenges; +using FModel.Quest; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -15,6 +18,7 @@ using System.Net; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; +using System.Threading; namespace FModel { @@ -25,6 +29,8 @@ namespace FModel private static string ItemName; private static List afterItems; public static string[] SelectedArray; + public static string[] challengesArray; + public static Dictionary questStageDict; PrivateFontCollection pfc = new PrivateFontCollection(); StringFormat centeredString = new StringFormat(); @@ -644,6 +650,63 @@ namespace FModel } } + private void loopStageQuest(string qAssetType, string qAssetName) + { + if (qAssetType == "Quest") + { + var filesPath = Directory.GetFiles(docPath + "\\Extracted", qAssetName + ".*", SearchOption.AllDirectories).Where(x => !x.EndsWith(".png")).FirstOrDefault(); + if (!File.Exists(filesPath)) + { + jwpmProcess("extract \"" + Properties.Settings.Default.FortnitePAKs + "\\" + currentPAK + "\" \"" + qAssetName + "\" \"" + docPath + "\""); + filesPath = Directory.GetFiles(docPath + "\\Extracted", qAssetName + ".*", SearchOption.AllDirectories).Where(x => !x.EndsWith(".png")).FirstOrDefault(); + } + try + { + if (filesPath != null) + { + jwpmProcess("serialize \"" + filesPath.Substring(0, filesPath.LastIndexOf('.')) + "\""); + var filesJSON3 = Directory.GetFiles(docPath, qAssetName + ".json", SearchOption.AllDirectories).FirstOrDefault(); + if (filesJSON3 != null) + { + var json3 = JToken.Parse(File.ReadAllText(filesJSON3)).ToString(); + File.Delete(filesJSON3); + + var questParser2 = QuestParser.FromJson(json3); + for (int p2 = 0; p2 < questParser2.Length; p2++) + { + for (int pp = 0; pp < questParser2[p2].Objectives.Length; pp++) + { + if (!questStageDict.ContainsKey(questParser2[p2].Objectives[pp].Description)) //AVOID DUPLICATA, THANKS EPIC... + { + questStageDict.Add(questParser2[p2].Objectives[pp].Description, questParser2[p2].Objectives[pp].Count); + AppendText(questParser2[p2].Objectives[pp].Description, Color.SteelBlue); + AppendText("\t\tCount: " + questParser2[p2].Objectives[pp].Count, Color.DarkRed, true); + } + for (int ppp = 0; ppp < questParser2[p2].Rewards.Length; ppp++) + { + loopStageQuest(questParser2[p2].Rewards[ppp].ItemPrimaryAssetId.PrimaryAssetType.Name, questParser2[p2].Rewards[ppp].ItemPrimaryAssetId.PrimaryAssetName); + } + } + } + } + else + { + AppendText("✗ ", Color.Red); + AppendText("No serialized file found", Color.Black, true); + } + } + } + catch (IndexOutOfRangeException) + { + AppendText("[IndexOutOfRangeException] ", Color.Red); + AppendText("Can't extract ", Color.Black); + AppendText(qAssetName, Color.SteelBlue); + AppendText(" in ", Color.Black); + AppendText(PAKsComboBox.SelectedItem.ToString(), Color.DarkRed, true); + } + } + } + public static string currentItem; private async void ExtractAssetButton_Click(object sender, EventArgs e) { @@ -652,6 +715,7 @@ namespace FModel scintilla1.Text = ""; ItemIconPictureBox.Image = null; + questStageDict = new Dictionary(); if (!Directory.Exists(docPath + "\\Extracted\\")) //Create Extracted Subfolder Directory.CreateDirectory(docPath + "\\Extracted\\"); @@ -1911,6 +1975,83 @@ namespace FModel } for (int ii = 0; ii < IDParser.Length; ii++) { + if (IDParser[ii].ExportType == "FortChallengeBundleItemDefinition") + { + var ChallengeParser = ChallengeBundleIdParser.FromJson(json); + AppendText("Parsing...Please wait while extracting all challenges\n", Color.Black, true); + + for (int iii = 0; iii < ChallengeParser.Length; iii++) + { + challengesArray = new string[ChallengeParser[iii].QuestInfos.Length]; + for (int y = 0; y < ChallengeParser[iii].QuestInfos.Length; y++) + { + string cName = Path.GetFileName(ChallengeParser[iii].QuestInfos[y].QuestDefinition.AssetPathName); + challengesArray[y] = cName.Substring(cName.LastIndexOf('.') + 1); + } + + for (int w = 0; w < challengesArray.Length; w++) + { + var filesPath = Directory.GetFiles(docPath + "\\Extracted", challengesArray[w] + ".*", SearchOption.AllDirectories).Where(x => !x.EndsWith(".png")).FirstOrDefault(); + if (!File.Exists(filesPath)) + { + await Task.Run(() => + { + jwpmProcess("extract \"" + Properties.Settings.Default.FortnitePAKs + "\\" + currentPAK + "\" \"" + challengesArray[w] + "\" \"" + docPath + "\""); + }); + filesPath = Directory.GetFiles(docPath + "\\Extracted", challengesArray[w] + ".*", SearchOption.AllDirectories).Where(x => !x.EndsWith(".png")).FirstOrDefault(); + } + try + { + if (filesPath != null) + { + await Task.Run(() => + { + jwpmProcess("serialize \"" + filesPath.Substring(0, filesPath.LastIndexOf('.')) + "\""); + }); + var filesJSON2 = Directory.GetFiles(docPath, challengesArray[w] + ".json", SearchOption.AllDirectories).FirstOrDefault(); + if (filesJSON2 != null) + { + var json2 = JToken.Parse(File.ReadAllText(filesJSON2)).ToString(); + File.Delete(filesJSON2); + + var questParser = QuestParser.FromJson(json2); + for (int p = 0; p < questParser.Length; p++) + { + string oldQuest = null; + for (int pp = 0; pp < questParser[p].Objectives.Length; pp++) + { + string newQuest = questParser[p].Objectives[pp].Description; + if (newQuest != oldQuest) + { + AppendText(questParser[p].Objectives[pp].Description, Color.SteelBlue); + AppendText("\t\tCount: " + questParser[p].Objectives[pp].Count, Color.DarkRed, true); + oldQuest = questParser[p].Objectives[pp].Description; + } + for (int ppp = 0; ppp < questParser[p].Rewards.Length; ppp++) + { + loopStageQuest(questParser[p].Rewards[ppp].ItemPrimaryAssetId.PrimaryAssetType.Name, questParser[p].Rewards[ppp].ItemPrimaryAssetId.PrimaryAssetName); + } + } + } + } + else + { + AppendText("✗ ", Color.Red); + AppendText("No serialized file found", Color.Black, true); + } + } + } + catch (IndexOutOfRangeException) + { + AppendText("[IndexOutOfRangeException] ", Color.Red); + AppendText("Can't extract ", Color.Black); + AppendText(challengesArray[w], Color.SteelBlue); + AppendText(" in ", Color.Black); + AppendText(PAKsComboBox.SelectedItem.ToString(), Color.DarkRed, true); + } + } + } + } //ASSET IS A CHALLENGE => if (IDParser[ii].ExportType == "Texture2D") { AppendText("Parsing...", Color.Black, true); diff --git a/FModel/PAKWindow.resx b/FModel/PAKWindow.resx index 42097a9b..5ebce2b5 100644 --- a/FModel/PAKWindow.resx +++ b/FModel/PAKWindow.resx @@ -131,7 +131,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACc - BwAAAk1TRnQBSQFMAgEBAgEAATgBAAE4AQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + BwAAAk1TRnQBSQFMAgEBAgEAAUABAAFAAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA diff --git a/FModel/Parser/ChallengeBundleIdParser.cs b/FModel/Parser/ChallengeBundleIdParser.cs new file mode 100644 index 00000000..643a5639 --- /dev/null +++ b/FModel/Parser/ChallengeBundleIdParser.cs @@ -0,0 +1,157 @@ +// +// +// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// +// using FModel; +// +// var challengeBundleIdParser = ChallengeBundleIdParser.FromJson(jsonString); + +namespace FModel.Challenges +{ + using System; + using System.Collections.Generic; + + using System.Globalization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + public partial class ChallengeBundleIdParser + { + [JsonProperty("export_type")] + public string ExportType { get; set; } + + [JsonProperty("QuestInfos")] + public QuestInfo[] QuestInfos { get; set; } + + [JsonProperty("BundleCompletionRewards")] + public BundleCompletionReward[] BundleCompletionRewards { get; set; } + + [JsonProperty("DisplayStyle")] + public DisplayStyle DisplayStyle { get; set; } + + [JsonProperty("DisplayName")] + public string DisplayName { get; set; } + + [JsonProperty("SmallPreviewImage")] + public LargePreviewImage SmallPreviewImage { get; set; } + + [JsonProperty("LargePreviewImage")] + public LargePreviewImage LargePreviewImage { get; set; } + } + + public partial class BundleCompletionReward + { + [JsonProperty("CompletionCount")] + public long CompletionCount { get; set; } + + [JsonProperty("Rewards")] + public Reward[] Rewards { get; set; } + } + + public partial class Reward + { + [JsonProperty("ItemDefinition")] + public LargePreviewImage ItemDefinition { get; set; } + + [JsonProperty("TemplateId")] + public string TemplateId { get; set; } + + [JsonProperty("Quantity")] + public long Quantity { get; set; } + + [JsonProperty("RewardGiftBox")] + public RewardGiftBox RewardGiftBox { get; set; } + + [JsonProperty("IsChaseReward")] + public bool IsChaseReward { get; set; } + + [JsonProperty("RewardType")] + public string RewardType { get; set; } + } + + public partial class LargePreviewImage + { + [JsonProperty("asset_path_name")] + public string AssetPathName { get; set; } + + [JsonProperty("sub_path_string")] + public string SubPathString { get; set; } + } + + public partial class RewardGiftBox + { + [JsonProperty("GiftBoxToUse")] + public LargePreviewImage GiftBoxToUse { get; set; } + + [JsonProperty("GiftBoxFormatData")] + public object[] GiftBoxFormatData { get; set; } + } + + public partial class DisplayStyle + { + [JsonProperty("PrimaryColor")] + public ColorChallenge PrimaryColor { get; set; } + + [JsonProperty("SecondaryColor")] + public ColorChallenge SecondaryColor { get; set; } + + [JsonProperty("AccentColor")] + public ColorChallenge AccentColor { get; set; } + + [JsonProperty("DisplayImage")] + public LargePreviewImage DisplayImage { get; set; } + } + + public partial class ColorChallenge + { + [JsonProperty("r")] + public double R { get; set; } + + [JsonProperty("g")] + public double G { get; set; } + + [JsonProperty("b")] + public double B { get; set; } + + [JsonProperty("a")] + public long A { get; set; } + } + + public partial class QuestInfo + { + [JsonProperty("QuestDefinition")] + public LargePreviewImage QuestDefinition { get; set; } + + [JsonProperty("QuestUnlockType")] + public string QuestUnlockType { get; set; } + + [JsonProperty("UnlockValue")] + public long UnlockValue { get; set; } + + [JsonProperty("RewardGiftBox")] + public RewardGiftBox RewardGiftBox { get; set; } + } + + public partial class ChallengeBundleIdParser + { + public static ChallengeBundleIdParser[] FromJson(string json) => JsonConvert.DeserializeObject(json, FModel.Challenges.Converter.Settings); + } + + public static class Serialize + { + public static string ToJson(this ChallengeBundleIdParser[] self) => JsonConvert.SerializeObject(self, FModel.Challenges.Converter.Settings); + } + + internal static class Converter + { + public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + Converters = + { + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } + }, + }; + } +} diff --git a/FModel/Parser/ItemsIDParser.cs b/FModel/Parser/ItemsIDParser.cs index 0f124617..b9f73ddc 100644 --- a/FModel/Parser/ItemsIDParser.cs +++ b/FModel/Parser/ItemsIDParser.cs @@ -6,7 +6,7 @@ // // var itemsIdParser = ItemsIdParser.FromJson(jsonString); -namespace FModel +namespace FModel.Items { using System; using System.Collections.Generic; @@ -71,12 +71,12 @@ namespace FModel public partial class ItemsIdParser { - public static ItemsIdParser[] FromJson(string json) => JsonConvert.DeserializeObject(json, FModel.Converter.Settings); + public static ItemsIdParser[] FromJson(string json) => JsonConvert.DeserializeObject(json, FModel.Items.Converter.Settings); } public static class Serialize { - public static string ToJson(this ItemsIdParser[] self) => JsonConvert.SerializeObject(self, FModel.Converter.Settings); + public static string ToJson(this ItemsIdParser[] self) => JsonConvert.SerializeObject(self, FModel.Items.Converter.Settings); } internal static class Converter diff --git a/FModel/Parser/QuestParser.cs b/FModel/Parser/QuestParser.cs new file mode 100644 index 00000000..5666ddfb --- /dev/null +++ b/FModel/Parser/QuestParser.cs @@ -0,0 +1,199 @@ +// +// +// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// +// using FModel.Quest; +// +// var questParser = QuestParser.FromJson(jsonString); + +namespace FModel.Quest +{ + using System; + using System.Collections.Generic; + + using System.Globalization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + public partial class QuestParser + { + [JsonProperty("export_type")] + public string ExportType { get; set; } + + [JsonProperty("QuestType")] + public string QuestType { get; set; } + + [JsonProperty("bIncludedInCategories")] + public bool BIncludedInCategories { get; set; } + + [JsonProperty("Rewards")] + public Reward[] Rewards { get; set; } + + [JsonProperty("Objectives")] + public Objective[] Objectives { get; set; } + + [JsonProperty("CompletionText")] + public string CompletionText { get; set; } + + [JsonProperty("GrantToProfileType")] + public string GrantToProfileType { get; set; } + + [JsonProperty("DisplayName")] + public string DisplayName { get; set; } + + [JsonProperty("Description")] + public string Description { get; set; } + + [JsonProperty("GameplayTags")] + public GameplayTags GameplayTags { get; set; } + + [JsonProperty("SmallPreviewImage")] + public LargePreviewImage SmallPreviewImage { get; set; } + + [JsonProperty("LargePreviewImage")] + public LargePreviewImage LargePreviewImage { get; set; } + } + + public partial class GameplayTags + { + [JsonProperty("gameplay_tags")] + public string[] GameplayTagsGameplayTags { get; set; } + } + + public partial class LargePreviewImage + { + [JsonProperty("asset_path_name")] + public string AssetPathName { get; set; } + + [JsonProperty("sub_path_string")] + public string SubPathString { get; set; } + } + + public partial class Objective + { + [JsonProperty("BackendName")] + public string BackendName { get; set; } + + [JsonProperty("ObjectiveStatHandle")] + public ObjectiveStatHandle ObjectiveStatHandle { get; set; } + + [JsonProperty("AlternativeStatHandles")] + public object[] AlternativeStatHandles { get; set; } + + [JsonProperty("ItemEvent")] + public string ItemEvent { get; set; } + + [JsonProperty("bHidden")] + public bool BHidden { get; set; } + + [JsonProperty("bRequirePrimaryMissionCompletion")] + public bool BRequirePrimaryMissionCompletion { get; set; } + + [JsonProperty("bCanProgressInZone")] + public bool BCanProgressInZone { get; set; } + + [JsonProperty("bDisplayDynamicAnnouncementUpdate")] + public bool BDisplayDynamicAnnouncementUpdate { get; set; } + + [JsonProperty("DynamicStatusUpdateType")] + public string DynamicStatusUpdateType { get; set; } + + [JsonProperty("LinkVaultTab")] + public string LinkVaultTab { get; set; } + + [JsonProperty("LinkToItemManagement")] + public string LinkToItemManagement { get; set; } + + [JsonProperty("ItemReference")] + public LargePreviewImage ItemReference { get; set; } + + [JsonProperty("ItemTemplateIdOverride")] + public string ItemTemplateIdOverride { get; set; } + + [JsonProperty("LinkSquadID")] + public string LinkSquadId { get; set; } + + [JsonProperty("LinkSquadIndex")] + public long LinkSquadIndex { get; set; } + + [JsonProperty("Description")] + public string Description { get; set; } + + [JsonProperty("HudShortDescription")] + public string HudShortDescription { get; set; } + + [JsonProperty("HudIcon")] + public LargePreviewImage HudIcon { get; set; } + + [JsonProperty("Count")] + public long Count { get; set; } + + [JsonProperty("Stage")] + public long Stage { get; set; } + + [JsonProperty("DynamicStatusUpdatePercentInterval")] + public long DynamicStatusUpdatePercentInterval { get; set; } + + [JsonProperty("DynamicUpdateCompletionDelay")] + public long DynamicUpdateCompletionDelay { get; set; } + + [JsonProperty("ScriptedAction")] + public LargePreviewImage ScriptedAction { get; set; } + } + + public partial class ObjectiveStatHandle + { + [JsonProperty("DataTable")] + public string DataTable { get; set; } + + [JsonProperty("RowName")] + public string RowName { get; set; } + } + + public partial class Reward + { + [JsonProperty("ItemPrimaryAssetId")] + public ItemPrimaryAssetId ItemPrimaryAssetId { get; set; } + + [JsonProperty("Quantity")] + public long Quantity { get; set; } + } + + public partial class ItemPrimaryAssetId + { + [JsonProperty("PrimaryAssetType")] + public PrimaryAssetType PrimaryAssetType { get; set; } + + [JsonProperty("PrimaryAssetName")] + public string PrimaryAssetName { get; set; } + } + + public partial class PrimaryAssetType + { + [JsonProperty("Name")] + public string Name { get; set; } + } + + public partial class QuestParser + { + public static QuestParser[] FromJson(string json) => JsonConvert.DeserializeObject(json, FModel.Quest.Converter.Settings); + } + + public static class Serialize + { + public static string ToJson(this QuestParser[] self) => JsonConvert.SerializeObject(self, FModel.Quest.Converter.Settings); + } + + internal static class Converter + { + public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + Converters = + { + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } + }, + }; + } +} diff --git a/README.md b/README.md index 73bd888a..d96c3f93 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [.NET Framework 4.6.1](https://dotnet.microsoft.com/download/dotnet-framework-runtime/net461) ### How does it works **1.** Once you start the executable, you'll be asked to set your path to your Fortnite .PAK files. Meanwhile a `FModel` subfolder will be created in your `Documents` folder and it'll automatically download the latest version of the modded Fortnite Asset Parser in this subfolder. -![](https://i.imgur.com/oaceS8K.gif) +![](https://i.imgur.com/sO6G6Vy.gif) **2.** Restart the executable, select your .PAK file, enter the AES key and click **Load** - It will parse all Assets contained in the selected .PAK file with their respective path @@ -28,6 +28,8 @@ - Try to display the Asset as PNG - Asset is a **_Sound_**: - Try to convert the Asset to OGG and play the sound + - Asset is a **_Bundle Of Challenges_**: + - Will display all challenges' description & count needed to complete them - Asset is a **_Font_**: - Try to convert the Asset to OTF @@ -45,8 +47,9 @@ I'd highly suggest you to use [UModel](https://github.com/gildor2/UModel) instea ## TODO - [ ] Improve speed -- [x] Multithreading +- [x] Multithreading - Need improvements - [x] Filter for the items ListBox +- [x] Quest viewer or something - [ ] More settings - [ ] Stop button while extracting - [ ] Support for meshes