Added @NotOfficer fork locked to latest manifest

This commit is contained in:
iAmAsval 2020-09-26 19:46:24 +02:00
parent 643a02d879
commit 5aea683c8f
17 changed files with 240 additions and 58 deletions

View File

@ -134,7 +134,7 @@ namespace FModel.Creator.Bases
string IBase.DisplayName => DisplayName;
string IBase.Description => Description;
int IBase.Width => Size;
int IBase.Height => Size + AdditionalSize;
int IBase.Height => Size;
int IBase.Margin => Margin;
}
}

View File

@ -135,6 +135,7 @@
<PackageReference Include="CSCore" Version="1.2.1.2" />
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
<PackageReference Include="DotNetZip" Version="1.13.8" />
<PackageReference Include="EpicManifestParser" Version="1.2.0" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.0.1" />
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.6" />
<PackageReference Include="LZMA-SDK" Version="19.0.0" />

View File

@ -0,0 +1,38 @@
using EpicManifestParser.Objects;
using FModel.Utils;
using System;
using System.Threading.Tasks;
namespace FModel.Grabber.Manifests
{
static class ManifestGrabber
{
public static async Task<ManifestInfo> TryGetLatestManifestInfo()
{
if (IsExpired())
{
OAuth auth = await Endpoints.GetOAuthInfo();
if (auth != null)
{
Properties.Settings.Default.AccessToken = auth.AccessToken;
Properties.Settings.Default.LauncherExpiration = DateTimeOffset.Now.AddSeconds(Convert.ToDouble(auth.ExpiresIn)).ToUnixTimeMilliseconds();
Properties.Settings.Default.Save();
}
}
string ret = await Endpoints.GetStringEndpoint("https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/v2/platform/Windows/namespace/fn/catalogItem/4fe75bbc5a674f4f9b356b5c90567da5/app/Fortnite/label/Live", Properties.Settings.Default.AccessToken);
if (!string.IsNullOrEmpty(ret)) return new ManifestInfo(ret);
else return null;
}
private static bool IsExpired()
{
long currentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
if ((currentTime - 60000) >= Properties.Settings.Default.LauncherExpiration)
{
return true;
}
return false;
}
}
}

View File

@ -10,23 +10,29 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using EpicManifestParser.Objects;
using System.Text.RegularExpressions;
using FModel.Grabber.Manifests;
namespace FModel.Grabber.Paks
{
static class PaksGrabber
{
private static readonly Regex _pakFileRegex = new Regex(@"^FortniteGame/Content/Paks/.+\.pak$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
public static async Task PopulateMenu()
{
PopulateBase();
await Task.Run(() =>
await Task.Run(async () =>
{
if (string.IsNullOrEmpty(Properties.Settings.Default.PakPath))
{
Application.Current.Dispatcher.Invoke(delegate
await Application.Current.Dispatcher.InvokeAsync(delegate
{
var launcher = new FLauncher();
if ((bool)launcher.ShowDialog())
bool? result = launcher.ShowDialog();
if (result.HasValue && result.Value)
{
Properties.Settings.Default.PakPath = launcher.Path;
Properties.Settings.Default.Save();
@ -34,12 +40,51 @@ namespace FModel.Grabber.Paks
});
}
// define the current game thank to the pak path
Folders.SetGameName(Properties.Settings.Default.PakPath);
// Add Pak Files
if (Directory.Exists(Properties.Settings.Default.PakPath))
if (Properties.Settings.Default.PakPath.EndsWith(".manifest") && await ManifestGrabber.TryGetLatestManifestInfo().ConfigureAwait(false) is ManifestInfo manifestInfo)
{
var manifestData = await manifestInfo.DownloadManifestDataAsync();
Manifest manifest = new Manifest(manifestData, new ManifestOptions
{
ChunkBaseUri = new Uri("http://download.epicgames.com/Builds/Fortnite/CloudDir/ChunksV3/", UriKind.Absolute),
ChunkCacheDirectory = Directory.CreateDirectory(Path.Combine(Properties.Settings.Default.OutputPath, "PakChunks"))
});
int pakFiles = 0;
foreach (FileManifest fileManifest in manifest.FileManifests)
{
if (!_pakFileRegex.IsMatch(fileManifest.Name))
{
continue;
}
var pakFileName = fileManifest.Name.Replace('/', '\\');
PakFileReader pakFile = new PakFileReader(pakFileName, fileManifest.GetStream());
if (pakFiles++ == 0)
{
// define the current game thank to the pak path
Folders.SetGameName(pakFileName);
Globals.Game.Version = pakFile.Info.Version;
Globals.Game.SubVersion = pakFile.Info.SubVersion;
}
await Application.Current.Dispatcher.InvokeAsync(delegate
{
MenuItems.pakFiles.Add(new PakMenuItemViewModel
{
PakFile = pakFile,
IsEnabled = false
});
});
}
}
else if (Directory.Exists(Properties.Settings.Default.PakPath))
{
// define the current game thank to the pak path
Folders.SetGameName(Properties.Settings.Default.PakPath);
string[] paks = Directory.GetFiles(Properties.Settings.Default.PakPath, "*.pak");
for (int i = 0; i < paks.Length; i++)
{
@ -53,7 +98,7 @@ namespace FModel.Grabber.Paks
Globals.Game.SubVersion = pakFile.Info.SubVersion;
}
Application.Current.Dispatcher.Invoke(delegate
await Application.Current.Dispatcher.InvokeAsync(delegate
{
MenuItems.pakFiles.Add(new PakMenuItemViewModel
{
@ -126,4 +171,4 @@ namespace FModel.Grabber.Paks
MenuItems.pakFiles.Add(new Separator { });
}
}
}
}

View File

@ -71,11 +71,6 @@
<Image Source="Resources/cast-audio.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Mesh Viewer">
<MenuItem.Icon>
<Image Source="Resources/rotate-3d.png"/>
</MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem x:Name="FModel_MI_Assets_GoTo" Header="{x:Static properties:Resources.Directories}">
<MenuItem.Icon>

View File

@ -15,6 +15,7 @@ namespace PakReader.Pak
{
public FPakInfo Info { get; }
public Stream Stream { get; }
public string Directory { get; }
public string FileName { get; }
public bool CaseSensitive { get; }
@ -45,6 +46,7 @@ namespace PakReader.Pak
public PakFileReader(string path, Stream stream, bool caseSensitive = true)
{
Directory = Path.GetDirectoryName(path);
FileName = Path.GetFileName(path);
Stream = stream;
CaseSensitive = caseSensitive;
@ -164,7 +166,7 @@ namespace PakReader.Pak
MountPoint = IndexReader.ReadFString() ?? "";
if (MountPoint.StartsWith("../../.."))
{
MountPoint = MountPoint.Substring(8);
MountPoint = MountPoint[8..];
}
else
{
@ -209,7 +211,7 @@ namespace PakReader.Pak
MountPoint = reader.ReadFString();
if (MountPoint.StartsWith("../../.."))
{
MountPoint = MountPoint.Substring(8);
MountPoint = MountPoint[8..];
}
else
{
@ -278,7 +280,7 @@ namespace PakReader.Pak
{
var path = directoryEntry.Directory + hashIndexEntry.Filename;
if (path.StartsWith("/"))
path = path.Substring(1);
path = path[1..];
if (!CaseSensitive)
{
path = path.ToLowerInvariant();
@ -491,4 +493,4 @@ namespace PakReader.Pak
IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator();
bool IReadOnlyDictionary<string, FPakEntry>.TryGetValue(string key, out FPakEntry value) => Entries.TryGetValue(key, out value);
}
}
}

View File

@ -2184,16 +2184,6 @@ namespace FModel.Properties {
}
}
/// <summary>
/// Recherche une ressource localisée de type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap rotate_3d {
get {
object obj = ResourceManager.GetObject("rotate_3d", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Russian.
/// </summary>

View File

@ -1081,7 +1081,4 @@ It's now the most used free software to leak on Fortnite.</value>
<data name="SkipThisVersion" xml:space="preserve">
<value>Skip this Version</value>
</data>
<data name="rotate_3d" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\rotate-3d.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -12,7 +12,7 @@ namespace FModel.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -514,5 +514,29 @@ namespace FModel.Properties {
this["ReloadAesKeys"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string AccessToken {
get {
return ((string)(this["AccessToken"]));
}
set {
this["AccessToken"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public long LauncherExpiration {
get {
return ((long)(this["LauncherExpiration"]));
}
set {
this["LauncherExpiration"] = value;
}
}
}
}

View File

@ -125,5 +125,11 @@
<Setting Name="ReloadAesKeys" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AccessToken" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="LauncherExpiration" Type="System.Int64" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
</Settings>
</SettingsFile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,7 +1,9 @@
using FModel.Logger;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace FModel.Utils
@ -11,6 +13,8 @@ namespace FModel.Utils
public const string BENBOT_AES = "https://benbotfn.tk/api/v1/aes";
public const string BENBOT_HOTFIXES = "https://benbotfn.tk/api/v1/hotfixes";
public const string FMODEL_JSON = "https://dl.dropbox.com/s/sxyaqo6zu1drlea/FModel.json?dl=0";
public const string OAUTH_URL = "https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token";
private const string _BASIC_FN_TOKEN = "basic MzQ0NmNkNzI2OTRjNGE0NDg1ZDgxYjc3YWRiYjIxNDE6OTIwOWQ0YTVlMjVhNDU3ZmI5YjA3NDg5ZDMxM2I0MWE=";
public static async Task<string> GetStringEndpoint(string url) => await GetStringEndpoint(url, string.Empty);
public static async Task<string> GetStringEndpoint(string url, string token)
@ -21,7 +25,10 @@ namespace FModel.Utils
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(url)))
{
if (!string.IsNullOrEmpty(token))
{
request.Headers.Add("Authorization", $"bearer {token}");
request.Content = new StringContent("", Encoding.UTF8, "application/json");
}
try
{
@ -72,5 +79,41 @@ namespace FModel.Utils
return default;
}
public static async Task<OAuth> GetOAuthInfo()
{
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[Endpoints]", "[GET]", OAUTH_URL);
using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) })
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(OAUTH_URL)))
{
request.Headers.Add("Authorization", _BASIC_FN_TOKEN);
request.Content = new StringContent("grant_type=client_credentials", Encoding.UTF8, "application/x-www-form-urlencoded");
try
{
using HttpResponseMessage httpResponseMessage = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
using Stream stream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
if (httpResponseMessage.IsSuccessStatusCode)
{
return Streams.DeserializeJsonFromStream<OAuth>(stream);
}
}
catch (Exception)
{
/* TaskCanceledException
* HttpRequestException */
}
}
return default;
}
}
}
public class OAuth
{
[JsonProperty("access_token")]
public string AccessToken;
[JsonProperty("expires_in")]
public long ExpiresIn;
}
}

View File

@ -19,11 +19,11 @@ namespace FModel.Utils
public static void SetGameName(string pakPath)
{
int index = pakPath.LastIndexOf("\\Content\\Paks");
int index = pakPath.LastIndexOf("\\Content\\Paks", StringComparison.Ordinal);
if (index > -1)
{
string p = pakPath.Substring(0, index);
string game = p.Substring(p.LastIndexOf("\\") + 1);
string game = p.Substring(p.LastIndexOf('\\') + 1);
Globals.Game.ActualGame = game switch
{
"FortniteGame" => EGame.Fortnite,
@ -169,4 +169,4 @@ namespace FModel.Utils
return filePath;
}
}
}
}

View File

@ -4,6 +4,7 @@ using FModel.ViewModels.StatusBar;
using Newtonsoft.Json;
using PakReader;
using PakReader.Parsers.Objects;
using System;
using System.Collections.Generic;
namespace FModel.Utils
@ -50,7 +51,7 @@ namespace FModel.Utils
if (menuItem.PakFile.Info.EncryptionKeyGuid.Equals(new FGuid(0u, 0u, 0u, 0u)) &&
staticKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var sKey))
{
sKey = sKey.StartsWith("0x") ? sKey.Substring(2).ToUpperInvariant() : sKey.ToUpperInvariant();
sKey = sKey.StartsWith("0x") ? sKey[2..].ToUpperInvariant() : sKey.ToUpperInvariant();
try
{
// i can use TestAesKey here but that means it's gonna test here then right after to set the key
@ -70,10 +71,16 @@ namespace FModel.Utils
}
}
string trigger = $"{Properties.Settings.Default.PakPath.Substring(Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName())).Replace("\\", "/")}/{menuItem.PakFile.FileName}";
string trigger;
{
if (Properties.Settings.Default.PakPath.EndsWith(".manifest"))
trigger = $"{menuItem.PakFile.Directory.Replace('\\', '/')}/{menuItem.PakFile.FileName}";
else
trigger = $"{Properties.Settings.Default.PakPath[Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName(), StringComparison.Ordinal)..].Replace("\\", "/")}/{menuItem.PakFile.FileName}";
}
if (dynamicKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var gameDict) && gameDict.TryGetValue(trigger, out var key))
{
string dKey = key.StartsWith("0x") ? key.Substring(2).ToUpperInvariant() : key.ToUpperInvariant();
string dKey = key.StartsWith("0x") ? key[2..].ToUpperInvariant() : key.ToUpperInvariant();
try
{
// i can use TestAesKey here but that means it's gonna test here then right after to set the key
@ -96,4 +103,4 @@ namespace FModel.Utils
}
}
}
}
}

View File

@ -5,6 +5,7 @@ using FModel.ViewModels.MenuItem;
using FModel.Windows.CustomNotifier;
using Newtonsoft.Json;
using PakReader.Pak;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows;
@ -60,12 +61,20 @@ namespace FModel.Windows.AESManager
foreach (PakFileReader pak in MenuItems.pakFiles.GetDynamicPakFileReaders())
{
gridRow++;
string trigger = $"{Properties.Settings.Default.PakPath.Substring(Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName())).Replace("\\", "/")}/{pak.FileName}";
string trigger;
{
if (Properties.Settings.Default.PakPath.EndsWith(".manifest"))
trigger = $"{pak.Directory.Replace('\\', '/')}/{pak.FileName}";
else
trigger = $"{Properties.Settings.Default.PakPath[Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName(), StringComparison.Ordinal)..].Replace("\\", "/")}/{pak.FileName}";
}
string key;
if (dynamicAesKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var gameDict) && gameDict.TryGetValue(trigger, out var dKey))
key = dKey;
else
key = "";
{
if (dynamicAesKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var gameDict) && gameDict.TryGetValue(trigger, out var dKey))
key = dKey;
else
key = "";
}
DebugHelper.WriteLine("{0} {1} {2} {3} {4}", "[FModel]", "[Window]", "[AES Manager]", "[GET]", $"{pak.FileName} with key: {key}");
DynamicKeys_Grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
@ -116,7 +125,13 @@ namespace FModel.Windows.AESManager
dynamicAesKeys[Globals.Game.ActualGame.ToString()] = new Dictionary<string, string>();
foreach (PakFileReader pak in MenuItems.pakFiles.GetDynamicPakFileReaders())
{
string trigger = $"{Properties.Settings.Default.PakPath.Substring(Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName())).Replace("\\", "/")}/{pak.FileName}";
string trigger;
{
if (Properties.Settings.Default.PakPath.EndsWith(".manifest"))
trigger = $"{pak.Directory.Replace('\\', '/')}/{pak.FileName}";
else
trigger = $"{Properties.Settings.Default.PakPath[Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName(), StringComparison.Ordinal)..].Replace("\\", "/")}/{pak.FileName}";
}
TextBox textBox = DependencyObjects.FindChild<TextBox>(this, $"pakchunk{Regex.Match(pak.FileName[0..^4], @"\d+").Value}");
if (!string.IsNullOrEmpty(textBox.Text))
{
@ -162,12 +177,20 @@ namespace FModel.Windows.AESManager
foreach (PakFileReader pak in MenuItems.pakFiles.GetDynamicPakFileReaders())
{
string trigger = $"{Properties.Settings.Default.PakPath.Substring(Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName())).Replace("\\", "/")}/{pak.FileName}";
string trigger;
{
if (Properties.Settings.Default.PakPath.EndsWith(".manifest"))
trigger = $"{pak.Directory.Replace('\\', '/')}/{pak.FileName}";
else
trigger = $"{Properties.Settings.Default.PakPath[Properties.Settings.Default.PakPath.LastIndexOf(Folders.GetGameName(), StringComparison.Ordinal)..].Replace("\\", "/")}/{pak.FileName}";
}
string key;
if (dynamicAesKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var gameDict) && gameDict.TryGetValue(trigger, out var dKey))
key = dKey;
else
key = "";
{
if (dynamicAesKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var gameDict) && gameDict.TryGetValue(trigger, out var dKey))
key = dKey;
else
key = "";
}
DebugHelper.WriteLine("{0} {1} {2} {3} {4}", "[FModel]", "[Window]", "[AES Manager]", "[UPDATE]", $"{pak.FileName} with key: {key}");
TextBox textBox = DependencyObjects.FindChild<TextBox>(this, $"pakchunk{Regex.Match(pak.FileName[0..^4], @"\d+").Value}");
@ -181,4 +204,4 @@ namespace FModel.Windows.AESManager
}
}
}
}
}

View File

@ -63,7 +63,7 @@
Content="{x:Static properties:Resources.Path}"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox x:Name="GamesPath_TxtBox" Grid.Row="1" Grid.Column="2"
TextWrapping="NoWrap" VerticalAlignment="Top" Foreground="#FFEFEFEF" Margin="5,3,5,0"/>
TextWrapping="NoWrap" VerticalAlignment="Top" Foreground="#FFEFEFEF" Margin="5,3,5,0" TextChanged="OnTextChange"/>
<Button Grid.Row="1" Grid.Column="3" Content="..." Height="20" Width="20" VerticalAlignment="Top" Margin="5,3,5,0" Click="OnInputClick"/>
</Grid>
</GroupBox>
@ -72,4 +72,4 @@
Content="{x:Static properties:Resources.OK}"
HorizontalAlignment="Right" Width="80" Click="OnClick"/>
</Grid>
</Window>
</Window>

View File

@ -47,6 +47,8 @@ namespace FModel.Windows.Launcher
ComboBoxVm.gamesCbViewModel.Add(new ComboBoxViewModel { Id = i++, Content = "Fortnite [EGL2]", Property = egl2FilesPath });
}
ComboBoxVm.gamesCbViewModel.Add(new ComboBoxViewModel { Id = i++, Content = "Fortnite [LIVE]", Property = "latest.manifest" });
string valorantFilesPath = Paks.GetValorantPakFilesPath();
if (!string.IsNullOrEmpty(valorantFilesPath))
{
@ -62,7 +64,7 @@ namespace FModel.Windows.Launcher
Globals.gNotifier.ShowCustomMessage("Borderlands 3", Properties.Resources.PathAutoDetected, "/FModel;component/Resources/borderlands3.ico");
ComboBoxVm.gamesCbViewModel.Add(new ComboBoxViewModel { Id = i++, Content = "Borderlands 3", Property = borderlands3FilesPath });
}
string minecraftdungeonsFilesPath = Paks.GetMinecraftDungeonsPakFilesPath();
if (!string.IsNullOrEmpty(minecraftdungeonsFilesPath))
{
@ -128,10 +130,19 @@ namespace FModel.Windows.Launcher
UseDescriptionForTitle = true
};
if ((bool)dialog.ShowDialog(this))
bool? result = dialog.ShowDialog();
if (result.HasValue && result.Value)
{
GamesPath_TxtBox.Text = dialog.SelectedPath;
}
}
private void OnTextChange(object sender, TextChangedEventArgs e)
{
if (e.Source is TextBox text)
{
text.IsReadOnly = text.Text == "latest.manifest";
}
}
}
}
}