added the fucking update mode

This commit is contained in:
Asval 2019-11-11 17:10:39 +01:00
parent 6edaf437ad
commit 5ac94fa91d
15 changed files with 747 additions and 21 deletions

View File

@ -94,6 +94,94 @@
<setting name="FAutoSaveImg" serializeAs="String"> <setting name="FAutoSaveImg" serializeAs="String">
<value>False</value> <value>False</value>
</setting> </setting>
<setting name="FUM_AssetsType" serializeAs="String">
<value>{
"[BR] Cosmetics": {
"Path": "/FortniteGame/Content/Athena/Items/Cosmetics/",
"isChecked": "True"
},
"[BR] Cosmetics Variants": {
"Path": "/FortniteGame/Content/Athena/Items/CosmeticVariantTokens/",
"isChecked": "True"
},
"[BR] Banners": {
"Path": "/FortniteGame/Content/Athena/Items/BannerToken/",
"isChecked": "True"
},
"[BR] Challenges": {
"Path": "/FortniteGame/Content/Athena/Items/ChallengeBundles/",
"isChecked": "True"
},
"[BR] Consumables": {
"Path": "/FortniteGame/Content/Athena/Items/Consumables/",
"isChecked": "False"
},
"[BR] Traps": {
"Path": "/FortniteGame/Content/Athena/Items/Traps/",
"isChecked": "False"
},
"[BR] Weapons": {
"Path": "/FortniteGame/Content/Athena/Items/Weapons/",
"isChecked": "True"
},
"[BR] 2D Assets": {
"Path": "/FortniteGame/Content/2dAssets/",
"isChecked": "True"
},
"[BR] Featured Images": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/BattleRoyale/FeaturedItems/",
"isChecked": "False"
},
"[STW] Heroes": {
"Path": "/FortniteGame/Content/Heroes/",
"isChecked": "False"
},
"[STW] Defenders": {
"Path": "/FortniteGame/Content/Items/Defenders/",
"isChecked": "False"
},
"[STW] Schematics": {
"Path": "/FortniteGame/Content/Items/Schematics/",
"isChecked": "False"
},
"[STW] Traps": {
"Path": "/FortniteGame/Content/Items/Traps/",
"isChecked": "False"
},
"[STW] Weapons": {
"Path": "/FortniteGame/Content/Items/Weapons/",
"isChecked": "False"
},
"[STW] Ingredients": {
"Path": "/FortniteGame/Content/Items/Ingredients/",
"isChecked": "False"
},
"[STW] Persistent Resources": {
"Path": "/FortniteGame/Content/Items/PersistentResources/",
"isChecked": "False"
},
"[STW] CardPacks": {
"Path": "/FortniteGame/Content/Items/CardPacks/",
"isChecked": "False"
},
"Tokens": {
"Path": "/FortniteGame/Content/Items/Tokens/",
"isChecked": "False"
},
"Icons": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/Icons/",
"isChecked": "False"
},
"Additional Banners": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/Banner/",
"isChecked": "False"
},
"Additional Loading Screens": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/LoadingScreens/",
"isChecked": "False"
}
}</value>
</setting>
</FModel.Properties.Settings> </FModel.Properties.Settings>
</userSettings> </userSettings>
<runtime> <runtime>

View File

@ -152,6 +152,9 @@
<Compile Include="Forms\FModel_Settings.xaml.cs"> <Compile Include="Forms\FModel_Settings.xaml.cs">
<DependentUpon>FModel_Settings.xaml</DependentUpon> <DependentUpon>FModel_Settings.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Forms\FModel_UpdateMode.xaml.cs">
<DependentUpon>FModel_UpdateMode.xaml</DependentUpon>
</Compile>
<Compile Include="Forms\HexViewer\BaseByte.cs" /> <Compile Include="Forms\HexViewer\BaseByte.cs" />
<Compile Include="Forms\HexViewer\Core\BookMark.cs" /> <Compile Include="Forms\HexViewer\Core\BookMark.cs" />
<Compile Include="Forms\HexViewer\Core\Bytes\ByteConverters.cs" /> <Compile Include="Forms\HexViewer\Core\Bytes\ByteConverters.cs" />
@ -417,6 +420,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Forms\FModel_UpdateMode.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Forms\HexViewer\BrushesDictionary.xaml"> <Page Include="Forms\HexViewer\BrushesDictionary.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@ -44,7 +44,7 @@
<MenuItem x:Name="MI_BackupPAKs" Header="Backup PAKs" Click="MI_BackupPAKs_Click" IsEnabled="False"/> <MenuItem x:Name="MI_BackupPAKs" Header="Backup PAKs" Click="MI_BackupPAKs_Click" IsEnabled="False"/>
<Separator/> <Separator/>
<MenuItem x:Name="MI_DifferenceMode" Header="Difference Mode" IsCheckable="True" Checked="MI_Change_Header" Unchecked="MI_Change_Header"/> <MenuItem x:Name="MI_DifferenceMode" Header="Difference Mode" IsCheckable="True" Checked="MI_Change_Header" Unchecked="MI_Change_Header"/>
<MenuItem x:Name="MI_UpdateMode" Header="Lazy Mode" IsCheckable="True" Checked="MI_Change_Header" Unchecked="MI_Change_Header" IsEnabled="False"/> <MenuItem x:Name="MI_UpdateMode" Header="Update Mode" IsCheckable="True" Checked="MI_Change_Header" Unchecked="MI_Change_Header" IsEnabled="False"/>
<Separator/> <Separator/>
<MenuItem x:Name="MI_Settings" Header="Settings" InputGestureText="F1" Command="{x:Static commands:FModel_Commands.OpenSettings}"> <MenuItem x:Name="MI_Settings" Header="Settings" InputGestureText="F1" Command="{x:Static commands:FModel_Commands.OpenSettings}">
<MenuItem.Icon> <MenuItem.Icon>

View File

@ -155,7 +155,8 @@ namespace FModel
//LOAD AND EXTRACT DIFF //LOAD AND EXTRACT DIFF
if (MI_DifferenceMode.IsChecked && MI_UpdateMode.IsChecked) if (MI_DifferenceMode.IsChecked && MI_UpdateMode.IsChecked)
{ {
//not done yet await PAKsLoader.LoadDifference();
await AssetsLoader.ExtractUpdateMode();
} }
} }
private void MI_ReloadAESs_Click(object sender, RoutedEventArgs e) private void MI_ReloadAESs_Click(object sender, RoutedEventArgs e)
@ -250,10 +251,21 @@ namespace FModel
{ {
MI_LoadAllPAKs.Header = "Load And Extract Difference"; MI_LoadAllPAKs.Header = "Load And Extract Difference";
MI_UpdateMode.IsEnabled = true; MI_UpdateMode.IsEnabled = true;
MI_Auto_Save_Images.IsChecked = true; //auto save images
if (MI_DifferenceMode.IsChecked && MI_UpdateMode.IsChecked)
{
if (!FormsUtility.IsWindowOpen<Window>("Update Mode"))
{
new FModel_UpdateMode().Show();
}
else { FormsUtility.GetOpenedWindow<Window>("Update Mode").Focus(); }
}
} }
if (!MI_UpdateMode.IsChecked) if (!MI_UpdateMode.IsChecked)
{ {
MI_LoadAllPAKs.Header = "Load Difference"; MI_LoadAllPAKs.Header = "Load Difference";
MI_Auto_Save_Images.IsChecked = false;
} }
//BOTH //BOTH

View File

@ -7,7 +7,7 @@
mc:Ignorable="d" mc:Ignorable="d"
Title="Search" Title="Search"
Height="700" Height="700"
Width="1000" Width="1100"
Style="{StaticResource {x:Type Window}}" Style="{StaticResource {x:Type Window}}"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
Icon="/FModel;component/Logo.ico" Icon="/FModel;component/Logo.ico"
@ -38,12 +38,13 @@
<Separator/> <Separator/>
<MenuItem x:Name="RC_Properties" Header="Properties" Click="RC_Properties_Click"> <MenuItem x:Name="RC_Properties" Header="Properties" Click="RC_Properties_Click">
<MenuItem.Icon> <MenuItem.Icon>
<Image Source="/FModel;component/Resources/StatusAnnotations_Information_16xLG_color.png" Width="16" Height="16"/> <Image Source="/FModel;component/Resources/info_16x.png" Width="16" Height="16"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
</DataGrid> </DataGrid>
<Button x:Name="GoTo_Button" Content="Go To Selected Asset" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Click="GoTo_Button_Click"/> <Button x:Name="GoTo_Button" Content="Go To Selected Asset" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Width="120" Click="GoTo_Button_Click" Margin="10,0,0,0"/>
<Label x:Name="FoundNumber_Label" Content="Found 0 asset" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,10,0" Padding="0"/>
</Grid> </Grid>
</Window> </Window>

View File

@ -9,6 +9,7 @@ using System.IO;
using FModel.Methods.Assets; using FModel.Methods.Assets;
using FProp = FModel.Properties.Settings; using FProp = FModel.Properties.Settings;
using System; using System;
using System.Globalization;
namespace FModel.Forms namespace FModel.Forms
{ {
@ -57,26 +58,34 @@ namespace FModel.Forms
} }
} }
private static async Task PopulateDataGrid() private async Task PopulateDataGrid()
{ {
Dictionary<string, string> IfExistChecker = new Dictionary<string, string>(); Dictionary<string, string> IfExistChecker = new Dictionary<string, string>();
int i = 0;
await Task.Run(() => await Task.Run(() =>
{ {
if (!string.IsNullOrEmpty(FWindow.FCurrentPAK)) if (!string.IsNullOrEmpty(FWindow.FCurrentPAK))
{ {
FillList(PAKEntries.PAKToDisplay[FWindow.FCurrentPAK], IfExistChecker); FPakEntry[] ohyeah = PAKEntries.PAKToDisplay[FWindow.FCurrentPAK];
FillList(ohyeah, IfExistChecker);
i = ohyeah.Length;
} }
else else
{ {
foreach (FPakEntry[] PAKsFileInfos in PAKEntries.PAKToDisplay.Values) foreach (FPakEntry[] PAKsFileInfos in PAKEntries.PAKToDisplay.Values)
{ {
FillList(PAKsFileInfos, IfExistChecker); FillList(PAKsFileInfos, IfExistChecker);
i += PAKsFileInfos.Length;
} }
} }
}).ContinueWith(TheTask => }).ContinueWith(TheTask =>
{ {
TasksUtility.TaskCompleted(TheTask.Exception); TasksUtility.TaskCompleted(TheTask.Exception);
}); });
//max = million
FoundNumber_Label.Content = $"Found {i.ToString("# ### ###", new NumberFormatInfo{ NumberGroupSeparator = " " }).Trim()} assets";
} }
private static void FillList(FPakEntry[] EntryArray, Dictionary<string, string> ExistChecker) private static void FillList(FPakEntry[] EntryArray, Dictionary<string, string> ExistChecker)

View File

@ -57,7 +57,7 @@
<ComboBoxItem Content="Flat"/> <ComboBoxItem Content="Flat"/>
<ComboBoxItem Content="Minimalist"/> <ComboBoxItem Content="Minimalist"/>
</ComboBox> </ComboBox>
<Border BorderThickness="1" Background="#FF252D36" HorizontalAlignment="Right" Height="164" Margin="398,0,0,0" Width="164" VerticalAlignment="Top"> <Border BorderThickness="1" BorderBrush="#7F748198" Background="#FF252D36" HorizontalAlignment="Right" Height="164" Margin="398,0,0,0" Width="164" VerticalAlignment="Top">
<Image x:Name="ImageBox_RarityPreview" Source="/FModel;component/Resources/Template_D_N.png" UseLayoutRounding="True"/> <Image x:Name="ImageBox_RarityPreview" Source="/FModel;component/Resources/Template_D_N.png" UseLayoutRounding="True"/>
</Border> </Border>
@ -94,7 +94,7 @@
<Slider x:Name="OpacityBanner_Slider" Style="{StaticResource Horizontal_Slider}" Thumb.DragCompleted="UpdateChallengeCustomTheme" HorizontalAlignment="Left" Margin="57,112,0,0" VerticalAlignment="Top" Maximum="255" Width="242" Background="#FF333C46" BorderBrush="#7F748198" Foreground="{x:Null}" IsEnabled="False"/> <Slider x:Name="OpacityBanner_Slider" Style="{StaticResource Horizontal_Slider}" Thumb.DragCompleted="UpdateChallengeCustomTheme" HorizontalAlignment="Left" Margin="57,112,0,0" VerticalAlignment="Top" Maximum="255" Width="242" Background="#FF333C46" BorderBrush="#7F748198" Foreground="{x:Null}" IsEnabled="False"/>
<CheckBox x:Name="bCustomChallenge" Content="Custom Theme" HorizontalAlignment="Left" Margin="6,59,0,0" VerticalAlignment="Top" Checked="EnableDisableCustomTheme" Unchecked="EnableDisableCustomTheme"/> <CheckBox x:Name="bCustomChallenge" Content="Custom Theme" HorizontalAlignment="Left" Margin="6,59,0,0" VerticalAlignment="Top" Checked="EnableDisableCustomTheme" Unchecked="EnableDisableCustomTheme"/>
<Border BorderThickness="1" Background="#FF252D36" HorizontalAlignment="Right" Height="105" Width="258" VerticalAlignment="Top" Margin="0,3,0,0"> <Border BorderThickness="1" BorderBrush="#7F748198" Background="#FF252D36" HorizontalAlignment="Right" Height="105" Width="258" VerticalAlignment="Top" Margin="0,3,0,0">
<Image x:Name="ImageBox_ChallengePreview" Source="/FModel;component/Resources/Template_Challenge.png" UseLayoutRounding="True"/> <Image x:Name="ImageBox_ChallengePreview" Source="/FModel;component/Resources/Template_Challenge.png" UseLayoutRounding="True"/>
</Border> </Border>
<Button x:Name="OpenChallengeTheme_Button" Content="Open Image" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="258" Click="OpenChallengeTheme_Button_Click" Margin="0,0,0,3"/> <Button x:Name="OpenChallengeTheme_Button" Content="Open Image" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="258" Click="OpenChallengeTheme_Button_Click" Margin="0,0,0,3"/>

View File

@ -1,7 +1,6 @@
using FModel.Methods.MessageBox; using FModel.Methods.MessageBox;
using System.Windows; using System.Windows;
using System; using System;
using FProp = FModel.Properties.Settings;
using System.Windows.Controls; using System.Windows.Controls;
using System.ComponentModel; using System.ComponentModel;
using System.Reflection; using System.Reflection;
@ -17,9 +16,7 @@ using Ookii.Dialogs.Wpf;
using System.Globalization; using System.Globalization;
using FModel.Methods.Assets.IconCreator; using FModel.Methods.Assets.IconCreator;
using ColorPickerWPF; using ColorPickerWPF;
using Newtonsoft.Json; using FProp = FModel.Properties.Settings;
using System.Collections.Generic;
using FModel.Methods.PAKs;
namespace FModel.Forms namespace FModel.Forms
{ {

View File

@ -0,0 +1,71 @@
<Window x:Class="FModel.Forms.FModel_UpdateMode"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FModel.Forms"
mc:Ignorable="d"
Title="Update Mode" Height="315" Width="700" UseLayoutRounding="True"
Style="{StaticResource {x:Type Window}}" WindowStartupLocation="CenterScreen" Icon="/FModel;component/Logo.ico" ResizeMode="CanMinimize" Loaded="Window_Loaded">
<Grid>
<ListBox x:Name="AssetsListBox" HorizontalAlignment="Left" Height="225" Margin="10,10,0,0" VerticalAlignment="Top" Width="250" ItemsSource="{Binding Assets}" SelectionChanged="AssetsListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="RC_Properties" Header="Properties" Click="RC_Properties_Click">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="/FModel;component/Resources/info_16x.png"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<Label Content="Name:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="265,10,0,0"/>
<TextBox x:Name="NameTextBox" Height="19" Margin="307,13,10,0" TextWrapping="NoWrap" VerticalAlignment="Top" Foreground="#FFEFEFEF"/>
<Label Content="Path:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="265,35,0,0"/>
<TextBox x:Name="PathTextBox" Text="/FortniteGame/Content/Athena/..." Height="19" Margin="307,38,10,0" TextWrapping="NoWrap" VerticalAlignment="Top" Foreground="#FFEFEFEF"/>
<Button x:Name="Button_AddAssetType" Content="Add" HorizontalAlignment="Right" Margin="0,62,10,0" VerticalAlignment="Top" Width="80" Height="19" Click="Button_AddAssetType_Click"/>
<Button x:Name="Button_RemoveAssetType" Content="Remove Selected" HorizontalAlignment="Right" Margin="0,62,95,0" VerticalAlignment="Top" Width="110" Height="19" IsEnabled="False" Click="Button_RemoveAssetType_Click"/>
<Border BorderThickness="1" BorderBrush="#7F748198" Background="#FF252D36" HorizontalAlignment="Left" Height="144" Margin="265,91,0,0" Width="411" VerticalAlignment="Top">
<Image x:Name="ImageBox_RarityPreview" Source="/FModel;component/Resources/Template_D_N.png" UseLayoutRounding="True" HorizontalAlignment="Right" Height="140" Width="140" Margin="0,0,1,0"/>
</Border>
<Label Content="Language:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="265,91,0,0"/>
<ComboBox x:Name="ComboBox_Language" HorizontalAlignment="Left" Margin="324,94,0,0" VerticalAlignment="Top" Width="202" BorderBrush="#7F748198" Background="#FF333C46">
<ComboBoxItem Content="English"/>
<ComboBoxItem Content="French"/>
<ComboBoxItem Content="German"/>
<ComboBoxItem Content="Italian"/>
<ComboBoxItem Content="Spanish"/>
<ComboBoxItem Content="Spanish (LA)"/>
<ComboBoxItem Content="Arabic"/>
<ComboBoxItem Content="Japanese"/>
<ComboBoxItem Content="Korean"/>
<ComboBoxItem Content="Polish"/>
<ComboBoxItem Content="Portuguese (Brazil)"/>
<ComboBoxItem Content="Russian"/>
<ComboBoxItem Content="Turkish"/>
<ComboBoxItem Content="Chinese (S)"/>
<ComboBoxItem Content="Traditional Chinese"/>
</ComboBox>
<Label Content="Rarity Design:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="265,116,0,0"/>
<ComboBox x:Name="ComboBox_Design" HorizontalAlignment="Left" Margin="341,119,0,0" VerticalAlignment="Top" Width="185" BorderBrush="#7F748198" Background="#FF333C46" IsReadOnly="True" SelectionChanged="UpdateImageBox">
<ComboBoxItem Content="Default"/>
<ComboBoxItem Content="Flat"/>
<ComboBoxItem Content="Minimalist"/>
</ComboBox>
<CheckBox x:Name="bFeaturedIcon" Content="Shop Item Icon" HorizontalAlignment="Left" Margin="438,143,0,0" VerticalAlignment="Top" Checked="UpdateImageBox" Unchecked="UpdateImageBox"/>
<CheckBox x:Name="bWatermarkIcon" Content="Watermark" HorizontalAlignment="Left" Margin="271,188,0,0" VerticalAlignment="Top" Checked="EnableDisableWatermark" Unchecked="EnableDisableWatermark"/>
<Button x:Name="OpenFile_Button" Content="+" HorizontalAlignment="Left" Margin="352,184,0,0" VerticalAlignment="Top" Width="19" IsEnabled="False" Click="OpenFile_Button_Click"/>
<Label x:Name="Watermark_Label" Content="File Name: " MaxWidth="240" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="376,183,0,0"/>
<Label Content="Opacity:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="265,205,0,0"/>
<Slider x:Name="Opacity_Slider" Style="{StaticResource Horizontal_Slider}" Thumb.DragCompleted="UpdateImageWithWatermark" HorizontalAlignment="Left" Margin="330,208,0,0" VerticalAlignment="Top" Maximum="255" Width="196" Background="#FF333C46" BorderBrush="#7F748198" Foreground="{x:Null}" IsEnabled="False"/>
<Button Content="OK" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="80" Click="Button_Click"/>
</Grid>
</Window>

View File

@ -0,0 +1,351 @@
using FModel.Methods;
using FModel.Methods.Assets;
using FModel.Methods.MessageBox;
using FModel.Methods.Utilities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using FProp = FModel.Properties.Settings;
namespace FModel.Forms
{
/// <summary>
/// Logique d'interaction pour FModel_UpdateMode.xaml
/// </summary>
public partial class FModel_UpdateMode : Window
{
#region CLASS
public class AssetProperties : INotifyPropertyChanged
{
public string Name { get; set; }
public string Path { get; set; }
//Provide change-notification for IsChecked
[JsonIgnore]
private bool _fIsChecked = false;
public bool IsChecked
{
get { return _fIsChecked; }
set
{
_fIsChecked = value;
this.OnPropertyChanged("IsChecked");
}
}
//Provide change-notification for IsSelected
[JsonIgnore]
private bool _fIsSelected = false;
public bool IsSelected
{
get { return _fIsSelected; }
set
{
_fIsSelected = value;
this.OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string strPropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
}
}
#endregion
#region ENUMS
enum LIndexes
{
[Description("English")]
English = 0,
[Description("French")]
French = 1,
[Description("German")]
German = 2,
[Description("Italian")]
Italian = 3,
[Description("Spanish")]
Spanish = 4,
[Description("Spanish (LA)")]
Spanish_LA = 5,
[Description("Arabic")]
Arabic = 6,
[Description("Japanese")]
Japanese = 7,
[Description("Korean")]
Korean = 8,
[Description("Polish")]
Polish = 9,
[Description("Portuguese (Brazil)")]
Portuguese_Brazil = 10,
[Description("Russian")]
Russian = 11,
[Description("Turkish")]
Turkish = 12,
[Description("Chinese (S)")]
Chinese_S = 13,
[Description("Traditional Chinese")]
Traditional_Chinese = 14
}
enum RIndexes
{
[Description("Default")]
Default = 0,
[Description("Flat")]
Flat = 1,
[Description("Minimalist")]
Minimalist = 2
}
public static T GetEnumValueFromDescription<T>(string description)
{
var type = typeof(T);
if (!type.IsEnum) { throw new ArgumentException(); }
FieldInfo[] fields = type.GetFields();
var field = fields
.SelectMany(f => f.GetCustomAttributes(
typeof(DescriptionAttribute), false), (
f, a) => new { Field = f, Att = a })
.Where(a => ((DescriptionAttribute)a.Att)
.Description == description).SingleOrDefault();
return field == null ? default(T) : (T)field.Field.GetRawConstantValue();
}
#endregion
public ObservableCollection<AssetProperties> Assets { get; set; }
public static Dictionary<string, Dictionary<string, string>> AssetsEntriesDict { get; set; }
public FModel_UpdateMode()
{
InitializeComponent();
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
FoldersUtility.CheckWatermark();
ComboBox_Language.SelectedIndex = (int)GetEnumValueFromDescription<LIndexes>(FProp.Default.FLanguage);
ComboBox_Design.SelectedIndex = (int)GetEnumValueFromDescription<RIndexes>(FProp.Default.FRarity_Design);
bFeaturedIcon.IsChecked = FProp.Default.FIsFeatured;
bWatermarkIcon.IsChecked = FProp.Default.FUseWatermark;
Watermark_Label.Content += Path.GetFileName(FProp.Default.FWatermarkFilePath);
Opacity_Slider.Value = FProp.Default.FWatermarkOpacity;
AssetsEntriesDict = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(FProp.Default.FUM_AssetsType);
Assets = new ObservableCollection<AssetProperties>();
foreach (KeyValuePair<string, Dictionary<string, string>> a in AssetsEntriesDict)
{
Assets.Add(new AssetProperties
{
Name = a.Key,
Path = a.Value["Path"],
IsChecked = bool.Parse(a.Value["isChecked"])
});
}
DataContext = this;
await UpdateImageWithWatermark();
}
private async void UpdateImageBox(object sender, RoutedEventArgs e)
{
await UpdateImageWithWatermark();
}
private async void UpdateImageWithWatermark(object sender, RoutedEventArgs e)
{
await UpdateImageWithWatermark();
}
private async Task UpdateImageWithWatermark()
{
bool watermarkEnabled = (bool)bWatermarkIcon.IsChecked;
bool isFeatured = (bool)bFeaturedIcon.IsChecked;
string rarityDesign = ((ComboBoxItem)ComboBox_Design.SelectedItem).Content.ToString();
int opacity = Convert.ToInt32(Opacity_Slider.Value);
double scale = FProp.Default.FWatermarkScale;
await Task.Run(() =>
{
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
//INITIALIZATION
drawingContext.DrawRectangle(Brushes.Transparent, null, new Rect(new Point(0, 0), new Size(515, 515)));
BitmapImage source = null;
switch (rarityDesign)
{
case "Default":
source = new BitmapImage(new Uri(isFeatured ? "pack://application:,,,/Resources/Template_D_F.png" : "pack://application:,,,/Resources/Template_D_N.png"));
break;
case "Flat":
source = new BitmapImage(new Uri(isFeatured ? "pack://application:,,,/Resources/Template_F_F.png" : "pack://application:,,,/Resources/Template_F_N.png"));
break;
case "Minimalist":
source = new BitmapImage(new Uri(isFeatured ? "pack://application:,,,/Resources/Template_M_F.png" : "pack://application:,,,/Resources/Template_M_N.png"));
break;
}
drawingContext.DrawImage(source, new Rect(new Point(0, 0), new Size(515, 515)));
if (!string.IsNullOrEmpty(FProp.Default.FWatermarkFilePath) && watermarkEnabled)
{
using (StreamReader image = new StreamReader(FProp.Default.FWatermarkFilePath))
{
if (image != null)
{
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.StreamSource = image.BaseStream;
bmp.EndInit();
drawingContext.DrawImage(ImagesUtility.CreateTransparency(bmp, opacity), new Rect(FProp.Default.FWatermarkXPos, FProp.Default.FWatermarkYPos, scale, scale));
}
}
}
}
if (drawingVisual != null)
{
RenderTargetBitmap RTB = new RenderTargetBitmap(515, 515, 96, 96, PixelFormats.Pbgra32);
RTB.Render(drawingVisual);
RTB.Freeze(); //We freeze to apply the RTB to our imagesource from the UI Thread
FWindow.FMain.Dispatcher.InvokeAsync(() =>
{
ImageBox_RarityPreview.Source = BitmapFrame.Create(RTB); //thread safe and fast af
});
}
}).ContinueWith(TheTask =>
{
TasksUtility.TaskCompleted(TheTask.Exception);
});
}
private async void EnableDisableWatermark(object sender, RoutedEventArgs e)
{
OpenFile_Button.IsEnabled = (bool)bWatermarkIcon.IsChecked;
Opacity_Slider.IsEnabled = (bool)bWatermarkIcon.IsChecked;
await UpdateImageWithWatermark();
}
private async void OpenFile_Button_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog openFiledialog = new Microsoft.Win32.OpenFileDialog();
openFiledialog.Title = "Choose your watermark";
openFiledialog.Multiselect = false;
openFiledialog.Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
if (openFiledialog.ShowDialog() == true)
{
Watermark_Label.Content = "File Name: " + Path.GetFileName(openFiledialog.FileName);
FProp.Default.FWatermarkFilePath = openFiledialog.FileName;
FProp.Default.Save();
await UpdateImageWithWatermark();
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AssetsEntriesDict = new Dictionary<string, Dictionary<string, string>>();
foreach (AssetProperties a in Assets)
{
AssetsEntriesDict[a.Name] = new Dictionary<string, string>();
AssetsEntriesDict[a.Name]["Path"] = a.Path;
AssetsEntriesDict[a.Name]["isChecked"] = a.IsChecked.ToString();
}
if (AssetEntries.AssetEntriesDict != null && !string.Equals(FProp.Default.FLanguage, ((ComboBoxItem)ComboBox_Language.SelectedItem).Content.ToString()))
{
AssetTranslations.SetAssetTranslation(((ComboBoxItem)ComboBox_Language.SelectedItem).Content.ToString());
}
FProp.Default.FLanguage = ((ComboBoxItem)ComboBox_Language.SelectedItem).Content.ToString();
FProp.Default.FRarity_Design = ((ComboBoxItem)ComboBox_Design.SelectedItem).Content.ToString();
FProp.Default.FIsFeatured = (bool)bFeaturedIcon.IsChecked;
FProp.Default.FUseWatermark = (bool)bWatermarkIcon.IsChecked;
FProp.Default.FWatermarkOpacity = Convert.ToInt32(Opacity_Slider.Value);
FProp.Default.FUM_AssetsType = JsonConvert.SerializeObject(AssetsEntriesDict, Formatting.Indented);
FProp.Default.Save();
Close();
}
private void AssetsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0) { ((ListBox)sender).ScrollIntoView(e.AddedItems[0]); }
Button_RemoveAssetType.IsEnabled = AssetsListBox.SelectedIndex >= 0;
}
private void Button_RemoveAssetType_Click(object sender, RoutedEventArgs e)
{
if (AssetsListBox.Items.Count > 0 && AssetsListBox.SelectedItems.Count > 0)
{
Assets.Remove(AssetsListBox.SelectedItem as AssetProperties);
}
}
private void Button_AddAssetType_Click(object sender, RoutedEventArgs e)
{
string path = PathTextBox.Text.Trim();
if (!path.StartsWith("/"))
path = path.Insert(0, "/");
if (!path.EndsWith("/"))
path += "/";
Assets.Add(new AssetProperties
{
Name = NameTextBox.Text,
Path = path,
IsSelected = true
});
}
private void RC_Properties_Click(object sender, RoutedEventArgs e)
{
if (AssetsListBox.SelectedIndex >= 0)
{
AssetProperties a = AssetsListBox.SelectedItem as AssetProperties;
string infos = GetAssetInfos(a);
if (DarkMessageBox.ShowYesNo(infos, a.Name, "Copy Properties", "OK") == MessageBoxResult.Yes)
{
Clipboard.SetText(infos);
new UpdateMyConsole(a.Name, CColors.Blue).Append();
new UpdateMyConsole("'s properties successfully copied", CColors.White, true).Append();
}
}
}
private static string GetAssetInfos(AssetProperties a)
{
StringBuilder sb = new StringBuilder();
sb.Append(
"\n- Name:\t\t" + a.Name +
"\n- Path:\t\t" + a.Path +
"\n- Checked:\t" + a.IsChecked +
"\n- Selected:\tTrue" +
"\n"
);
return sb.ToString();
}
}
}

View File

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Windows.Media; using System.Windows.Media;
using FProp = FModel.Properties.Settings;
namespace FModel.Methods.Assets namespace FModel.Methods.Assets
{ {
@ -56,7 +55,6 @@ namespace FModel.Methods.Assets
FWindow.FMain.Button_Extract.IsEnabled = true; FWindow.FMain.Button_Extract.IsEnabled = true;
FWindow.FMain.Button_Stop.IsEnabled = false; FWindow.FMain.Button_Stop.IsEnabled = false;
} }
public static async Task ExtractFoldersAndSub(string path) public static async Task ExtractFoldersAndSub(string path)
{ {
new UpdateMyProcessEvents("", "").Update(); new UpdateMyProcessEvents("", "").Update();
@ -127,8 +125,70 @@ namespace FModel.Methods.Assets
FWindow.FMain.Button_Extract.IsEnabled = true; FWindow.FMain.Button_Extract.IsEnabled = true;
FWindow.FMain.Button_Stop.IsEnabled = false; FWindow.FMain.Button_Stop.IsEnabled = false;
} }
public static async Task ExtractUpdateMode()
{
new UpdateMyProcessEvents("", "").Update();
FWindow.FMain.MI_UpdateMode.IsEnabled = true;
FWindow.FMain.Button_Extract.IsEnabled = false;
FWindow.FMain.Button_Stop.IsEnabled = true;
FWindow.FMain.AssetPropertiesBox_Main.Text = string.Empty;
FWindow.FMain.AssetPropertiesBox_Main.SyntaxHighlighting = ResourceLoader.LoadHighlightingDefinition("Json.xshd");
FWindow.FMain.ImageBox_Main.Source = null;
private static void LoadAsset(string assetPath) List<IEnumerable<string>> assetList = new List<IEnumerable<string>>();
foreach (FPakEntry[] PAKsFileInfos in PAKEntries.PAKToDisplay.Values)
{
IEnumerable<string> files = PAKsFileInfos
.Where(x => Forms.FModel_UpdateMode.AssetsEntriesDict.Any(c => bool.Parse(c.Value["isChecked"]) && x.Name.StartsWith(c.Value["Path"])))
.Select(x => x.Name);
if (files != null) { assetList.Add(files); }
}
TasksUtility.CancellableTaskTokenSource = new CancellationTokenSource();
CancellationToken cToken = TasksUtility.CancellableTaskTokenSource.Token;
await Task.Run(() =>
{
isRunning = true;
foreach (IEnumerable<string> filesFromOnePak in assetList)
{
foreach (string asset in filesFromOnePak.OrderBy(s => s))
{
cToken.ThrowIfCancellationRequested(); //if clicked on 'Stop' it breaks at the following item
string target;
if (asset.EndsWith(".uexp") || asset.EndsWith(".ubulk")) { continue; }
else if (!asset.EndsWith(".uasset"))
{
target = asset; //ini uproject locres etc
}
else
{
target = asset.Substring(0, asset.LastIndexOf(".")); //uassets
}
FWindow.FMain.Dispatcher.InvokeAsync(() => //ui thread because if not, FCurrentAsset isn't updated in time to Auto Save a JSON Data for example
{
FWindow.FCurrentAsset = Path.GetFileName(target);
});
LoadAsset(target);
}
}
}, cToken).ContinueWith(TheTask =>
{
TasksUtility.TaskCompleted(TheTask.Exception);
TasksUtility.CancellableTaskTokenSource.Dispose();
isRunning = false;
});
FWindow.FMain.MI_Auto_Save_Images.IsChecked = false;
FWindow.FMain.Button_Extract.IsEnabled = true;
FWindow.FMain.Button_Stop.IsEnabled = false;
new UpdateMyProcessEvents("All assets have been extracted successfully", "Success").Update();
}
public static void LoadAsset(string assetPath)
{ {
PakReader.PakReader reader = AssetsUtility.GetPakReader(assetPath); PakReader.PakReader reader = AssetsUtility.GetPakReader(assetPath);
if (reader != null) if (reader != null)

View File

@ -127,7 +127,7 @@ namespace FModel.Methods.PAKs
catch (Exception ex) catch (Exception ex)
{ {
if (string.Equals(ex.Message, "The AES key is invalid")) { UIHelper.DisplayError(); } if (string.Equals(ex.Message, "The AES key is invalid")) { UIHelper.DisplayError(); }
else { UIHelper.DisplayEmergencyError(ex); return; } else { new UpdateMyConsole(ex.Message, CColors.Red, true).Append(); return; }
break; break;
} }
@ -171,7 +171,7 @@ namespace FModel.Methods.PAKs
catch (Exception ex) catch (Exception ex)
{ {
if (string.Equals(ex.Message, "The AES key is invalid")) { UIHelper.DisplayError(Path.GetFileNameWithoutExtension(Pak.ThePAKPath), AESFromManager); } if (string.Equals(ex.Message, "The AES key is invalid")) { UIHelper.DisplayError(Path.GetFileNameWithoutExtension(Pak.ThePAKPath), AESFromManager); }
else { UIHelper.DisplayEmergencyError(ex); return; } else { new UpdateMyConsole(ex.Message, CColors.Red, true).Append(); return; }
continue; continue;
} }
@ -290,9 +290,9 @@ namespace FModel.Methods.PAKs
FWindow.FMain.ViewModel = srt; FWindow.FMain.ViewModel = srt;
}); });
new UpdateMyProcessEvents("All PAK files have been compared successfully", "Success").Update();
await FWindow.FMain.Dispatcher.InvokeAsync(() => await FWindow.FMain.Dispatcher.InvokeAsync(() =>
{ {
new UpdateMyProcessEvents("All PAK files have been compared successfully", "Success").Update();
FWindow.FMain.TreeView_Main.IsEnabled = true; FWindow.FMain.TreeView_Main.IsEnabled = true;
FWindow.FMain.MI_LoadAllPAKs.IsEnabled = true; FWindow.FMain.MI_LoadAllPAKs.IsEnabled = true;
FWindow.FMain.MI_BackupPAKs.IsEnabled = true; FWindow.FMain.MI_BackupPAKs.IsEnabled = true;

View File

@ -358,5 +358,47 @@ namespace FModel.Properties {
this["FAutoSaveImg"] = value; this["FAutoSaveImg"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("{\r\n \"[BR] Cosmetics\": {\r\n \"Path\": \"/FortniteGame/Content/Athena/Items/Cosmeti" +
"cs/\",\r\n \"isChecked\": \"True\"\r\n },\r\n \"[BR] Cosmetics Variants\": {\r\n \"Path\"" +
": \"/FortniteGame/Content/Athena/Items/CosmeticVariantTokens/\",\r\n \"isChecked\":" +
" \"True\"\r\n },\r\n \"[BR] Banners\": {\r\n \"Path\": \"/FortniteGame/Content/Athena/It" +
"ems/BannerToken/\",\r\n \"isChecked\": \"True\"\r\n },\r\n \"[BR] Challenges\": {\r\n \"" +
"Path\": \"/FortniteGame/Content/Athena/Items/ChallengeBundles/\",\r\n \"isChecked\":" +
" \"True\"\r\n },\r\n \"[BR] Consumables\": {\r\n \"Path\": \"/FortniteGame/Content/Athen" +
"a/Items/Consumables/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"[BR] Traps\": {\r\n \"" +
"Path\": \"/FortniteGame/Content/Athena/Items/Traps/\",\r\n \"isChecked\": \"False\"\r\n " +
" },\r\n \"[BR] Weapons\": {\r\n \"Path\": \"/FortniteGame/Content/Athena/Items/Weapon" +
"s/\",\r\n \"isChecked\": \"True\"\r\n },\r\n \"[BR] 2D Assets\": {\r\n \"Path\": \"/Fortni" +
"teGame/Content/2dAssets/\",\r\n \"isChecked\": \"True\"\r\n },\r\n \"[BR] Featured Imag" +
"es\": {\r\n \"Path\": \"/FortniteGame/Content/UI/Foundation/Textures/BattleRoyale/F" +
"eaturedItems/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"[STW] Heroes\": {\r\n \"Path\"" +
": \"/FortniteGame/Content/Heroes/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"[STW] Def" +
"enders\": {\r\n \"Path\": \"/FortniteGame/Content/Items/Defenders/\",\r\n \"isChecke" +
"d\": \"False\"\r\n },\r\n \"[STW] Schematics\": {\r\n \"Path\": \"/FortniteGame/Content/I" +
"tems/Schematics/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"[STW] Traps\": {\r\n \"Pat" +
"h\": \"/FortniteGame/Content/Items/Traps/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"[S" +
"TW] Weapons\": {\r\n \"Path\": \"/FortniteGame/Content/Items/Weapons/\",\r\n \"isChe" +
"cked\": \"False\"\r\n },\r\n \"[STW] Ingredients\": {\r\n \"Path\": \"/FortniteGame/Conte" +
"nt/Items/Ingredients/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"[STW] Persistent Res" +
"ources\": {\r\n \"Path\": \"/FortniteGame/Content/Items/PersistentResources/\",\r\n " +
" \"isChecked\": \"False\"\r\n },\r\n \"[STW] CardPacks\": {\r\n \"Path\": \"/FortniteGame/" +
"Content/Items/CardPacks/\",\r\n \"isChecked\": \"False\"\r\n },\r\n \"Tokens\": {\r\n \"" +
"Path\": \"/FortniteGame/Content/Items/Tokens/\",\r\n \"isChecked\": \"False\"\r\n },\r\n " +
" \"Icons\": {\r\n \"Path\": \"/FortniteGame/Content/UI/Foundation/Textures/Icons/\",\r" +
"\n \"isChecked\": \"False\"\r\n },\r\n \"Additional Banners\": {\r\n \"Path\": \"/Fortni" +
"teGame/Content/UI/Foundation/Textures/Banner/\",\r\n \"isChecked\": \"False\"\r\n },\r" +
"\n \"Additional Loading Screens\": {\r\n \"Path\": \"/FortniteGame/Content/UI/Founda" +
"tion/Textures/LoadingScreens/\",\r\n \"isChecked\": \"False\"\r\n }\r\n}")]
public string FUM_AssetsType {
get {
return ((string)(this["FUM_AssetsType"]));
}
set {
this["FUM_AssetsType"] = value;
}
}
} }
} }

View File

@ -86,5 +86,93 @@
<Setting Name="FAutoSaveImg" Type="System.Boolean" Scope="User"> <Setting Name="FAutoSaveImg" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value> <Value Profile="(Default)">False</Value>
</Setting> </Setting>
<Setting Name="FUM_AssetsType" Type="System.String" Scope="User">
<Value Profile="(Default)">{
"[BR] Cosmetics": {
"Path": "/FortniteGame/Content/Athena/Items/Cosmetics/",
"isChecked": "True"
},
"[BR] Cosmetics Variants": {
"Path": "/FortniteGame/Content/Athena/Items/CosmeticVariantTokens/",
"isChecked": "True"
},
"[BR] Banners": {
"Path": "/FortniteGame/Content/Athena/Items/BannerToken/",
"isChecked": "True"
},
"[BR] Challenges": {
"Path": "/FortniteGame/Content/Athena/Items/ChallengeBundles/",
"isChecked": "True"
},
"[BR] Consumables": {
"Path": "/FortniteGame/Content/Athena/Items/Consumables/",
"isChecked": "False"
},
"[BR] Traps": {
"Path": "/FortniteGame/Content/Athena/Items/Traps/",
"isChecked": "False"
},
"[BR] Weapons": {
"Path": "/FortniteGame/Content/Athena/Items/Weapons/",
"isChecked": "True"
},
"[BR] 2D Assets": {
"Path": "/FortniteGame/Content/2dAssets/",
"isChecked": "True"
},
"[BR] Featured Images": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/BattleRoyale/FeaturedItems/",
"isChecked": "False"
},
"[STW] Heroes": {
"Path": "/FortniteGame/Content/Heroes/",
"isChecked": "False"
},
"[STW] Defenders": {
"Path": "/FortniteGame/Content/Items/Defenders/",
"isChecked": "False"
},
"[STW] Schematics": {
"Path": "/FortniteGame/Content/Items/Schematics/",
"isChecked": "False"
},
"[STW] Traps": {
"Path": "/FortniteGame/Content/Items/Traps/",
"isChecked": "False"
},
"[STW] Weapons": {
"Path": "/FortniteGame/Content/Items/Weapons/",
"isChecked": "False"
},
"[STW] Ingredients": {
"Path": "/FortniteGame/Content/Items/Ingredients/",
"isChecked": "False"
},
"[STW] Persistent Resources": {
"Path": "/FortniteGame/Content/Items/PersistentResources/",
"isChecked": "False"
},
"[STW] CardPacks": {
"Path": "/FortniteGame/Content/Items/CardPacks/",
"isChecked": "False"
},
"Tokens": {
"Path": "/FortniteGame/Content/Items/Tokens/",
"isChecked": "False"
},
"Icons": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/Icons/",
"isChecked": "False"
},
"Additional Banners": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/Banner/",
"isChecked": "False"
},
"Additional Loading Screens": {
"Path": "/FortniteGame/Content/UI/Foundation/Textures/LoadingScreens/",
"isChecked": "False"
}
}</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -70,10 +70,10 @@
## How To Use ## How To Use
To run this program, you'll need [.NET Framework 4.7.1](https://dotnet.microsoft.com/download/dotnet-framework/net471) or newer installed on your computer. Once it's done, you can download the latest release [here](https://github.com/iAmAsval/FModel/releases/latest/download/FModel.zip). To run this program, you'll need [.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472) or newer installed on your computer. Once it's done, you can download the latest release [here](https://github.com/iAmAsval/FModel/releases/latest/download/FModel.zip).
1. Extract `FModel.exe` and `libSkiaSharp.dll` somewhere - __double click on FModel.exe__ 1. Extract `FModel.exe` and `libSkiaSharp.dll` somewhere - __double click on FModel.exe__
2. You're now asked to set the path to your Fortnite .PAK files - __click on Load and then on Settings__ 2. The path to your Fortnite .PAK files should be automatically detected but you can still set it manually - __click on Load and then on Settings__
3. You'll need the AES key in order to read the files - __click on AES Manager and enter the key under Static Key__ 3. You'll need the AES key in order to read the files - __click on AES Manager and enter the key under Static Key__
4. You can now load one or all .PAK files 4. You can now load one or all .PAK files
5. Navigate through the tree to find the Asset you want 5. Navigate through the tree to find the Asset you want