fn live hd texture download

This commit is contained in:
4sval 2023-05-10 16:29:18 +02:00
parent 42ad5ddb7a
commit b4f29562cd
5 changed files with 132 additions and 74 deletions

@ -1 +1 @@
Subproject commit 97174265894eddcb0d94ef570d36ddc559a8573a
Subproject commit 21df8a55d474f14148a35bc943e06f3fdc20c997

View File

@ -69,9 +69,9 @@ public partial class MainWindow
await _applicationView.CUE4Parse.InitInformation();
#endif
await Task.WhenAll(
Task.Run(() => _applicationView.CUE4Parse.VerifyConsoleVariables()),
Task.Run(() => _applicationView.CUE4Parse.VerifyVirtualCache()),
Task.Run(() => _applicationView.CUE4Parse.VerifyContentBuildManifest()),
_applicationView.CUE4Parse.VerifyConsoleVariables(),
_applicationView.CUE4Parse.VerifyVirtualCache(),
_applicationView.CUE4Parse.VerifyContentBuildManifest(),
_applicationView.CUE4Parse.InitMappings(),
_applicationView.InitImGuiSettings(newOrUpdated),
_applicationView.InitVgmStream(),
@ -84,9 +84,9 @@ public partial class MainWindow
).ConfigureAwait(false);
#if DEBUG
await _threadWorkerView.Begin(cancellationToken =>
_applicationView.CUE4Parse.Extract(cancellationToken,
"fortnitegame/Content/Characters/Player/Female/Medium/Bodies/F_MED_Ballerina/Meshes/F_MED_Ballerina.uasset"));
// await _threadWorkerView.Begin(cancellationToken =>
// _applicationView.CUE4Parse.Extract(cancellationToken,
// "fortnitegame/Content/Characters/Player/Female/Medium/Bodies/F_MED_Ballerina/Meshes/F_MED_Ballerina.uasset"));
#endif
}

View File

@ -13,7 +13,8 @@ public class EpicApiEndpoint : AbstractApiProvider
{
private const string _OAUTH_URL = "https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token";
private const string _BASIC_TOKEN = "basic MzQ0NmNkNzI2OTRjNGE0NDg1ZDgxYjc3YWRiYjIxNDE6OTIwOWQ0YTVlMjVhNDU3ZmI5YjA3NDg5ZDMxM2I0MWE=";
private const string _LAUNCHER_ASSETS = "https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/v2/platform/Windows/namespace/fn/catalogItem/4fe75bbc5a674f4f9b356b5c90567da5/app/Fortnite/label/Live";
private const string _APP_URL = "https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/v2/platform/Windows/namespace/fn/catalogItem/4fe75bbc5a674f4f9b356b5c90567da5/app/Fortnite/label/Live";
private const string _CBM_URL = "https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/Windows/5cb97847cee34581afdbc445400e2f77/FortniteContentBuilds";
public EpicApiEndpoint(RestClient client) : base(client) { }
@ -28,11 +29,30 @@ public class EpicApiEndpoint : AbstractApiProvider
}
}
var request = new FRestRequest(_LAUNCHER_ASSETS);
var request = new FRestRequest(_APP_URL);
request.AddHeader("Authorization", $"bearer {UserSettings.Default.LastAuthResponse.AccessToken}");
var response = await _client.ExecuteAsync(request, token).ConfigureAwait(false);
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, response.ResponseUri?.OriginalString);
return new ManifestInfo(response.Content);
return response.IsSuccessful ? new ManifestInfo(response.Content) : null;
}
public async Task<ContentBuildManifestInfo> GetContentBuildManifestAsync(CancellationToken token, string label)
{
if (await IsExpired().ConfigureAwait(false))
{
var auth = await GetAuthAsync(token).ConfigureAwait(false);
if (auth != null)
{
UserSettings.Default.LastAuthResponse = auth;
}
}
var request = new FRestRequest(_CBM_URL);
request.AddHeader("Authorization", $"bearer {UserSettings.Default.LastAuthResponse.AccessToken}");
request.AddQueryParameter("label", label);
var response = await _client.ExecuteAsync(request, token).ConfigureAwait(false);
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, response.ResponseUri?.OriginalString);
return response.IsSuccessful ? new ContentBuildManifestInfo(response.Content) : null;
}
public ManifestInfo GetManifest(CancellationToken token)
@ -40,6 +60,11 @@ public class EpicApiEndpoint : AbstractApiProvider
return GetManifestAsync(token).GetAwaiter().GetResult();
}
public ContentBuildManifestInfo GetContentBuildManifest(CancellationToken token, string label)
{
return GetContentBuildManifestAsync(token, label).GetAwaiter().GetResult();
}
private async Task<AuthResponse> GetAuthAsync(CancellationToken token)
{
var request = new FRestRequest(_OAUTH_URL, Method.Post);

View File

@ -120,6 +120,7 @@ public class CUE4ParseViewModel : ViewModel
public AssetsFolderViewModel AssetsFolder { get; }
public SearchViewModel SearchVm { get; }
public TabControlViewModel TabControl { get; }
public ConfigIni BuildInfo { get; }
public CUE4ParseViewModel(string gameDirectory)
{
@ -204,6 +205,7 @@ public class CUE4ParseViewModel : ViewModel
AssetsFolder = new AssetsFolderViewModel();
SearchVm = new SearchViewModel();
TabControl = new TabControlViewModel();
BuildInfo = new ConfigIni(nameof(BuildInfo));
}
public async Task Initialize()
@ -244,18 +246,19 @@ public class CUE4ParseViewModel : ViewModel
foreach (var fileManifest in manifest.FileManifests)
{
if (fileManifest.Name.Equals("Cloud/BuildInfo.ini", StringComparison.OrdinalIgnoreCase))
{
BuildInfo.Read(new StreamReader(fileManifest.GetStream()));
continue;
}
if (!_fnLive.IsMatch(fileManifest.Name)) continue;
//var casStream = manifest.FileManifests.FirstOrDefault(x => x.Name.Equals(fileManifest.Name.Replace(".utoc", ".ucas")));
//p.Initialize(fileManifest.Name, new[] {fileManifest.GetStream(), casStream.GetStream()});
p.Initialize(fileManifest.Name, new Stream[] { fileManifest.GetStream() }
, it => new FStreamArchive(it, manifest.FileManifests.First(x => x.Name.Equals(it)).GetStream(), p.Versions));
}
FLogger.Append(ELog.Information, () =>
FLogger.Text($"Fortnite has been loaded successfully in {manifest.ParseTime.TotalMilliseconds}ms", Constants.WHITE, true));
FLogger.Append(ELog.Warning, () =>
FLogger.Text($"Mappings must match '{manifest.BuildVersion}' in order to avoid errors", Constants.WHITE, true));
break;
}
case "ValorantLive":
@ -280,6 +283,9 @@ public class CUE4ParseViewModel : ViewModel
break;
case DefaultFileProvider d:
d.Initialize();
var buildInfoPath = Path.Combine(UserSettings.Default.GameDirectory, "..\\..\\..\\Cloud\\BuildInfo.ini");
if (File.Exists(buildInfoPath)) BuildInfo.Read(new StringReader(File.ReadAllText(buildInfoPath)));
break;
}
@ -429,108 +435,133 @@ public class CUE4ParseViewModel : ViewModel
}
private bool _cvaVerifDone { get; set; }
public void VerifyConsoleVariables()
public Task VerifyConsoleVariables()
{
if (_cvaVerifDone) return;
_cvaVerifDone = true;
if (_cvaVerifDone)
return Task.CompletedTask;
var inst = new List<InstructionToken>();
Provider.DefaultEngine.FindPropertyInstructions("ConsoleVariables", "a.StripAdditiveRefPose", inst);
if (inst.Count > 0 && inst[0].Value.Equals("1"))
return Task.Run(() =>
{
FLogger.Append(ELog.Warning, () =>
FLogger.Text("Additive animations have their reference pose stripped, which will lead to inaccurate preview and export", Constants.WHITE, true));
}
var inst = new List<InstructionToken>();
Provider.DefaultEngine.FindPropertyInstructions("ConsoleVariables", "a.StripAdditiveRefPose", inst);
if (inst.Count > 0 && inst[0].Value.Equals("1"))
{
FLogger.Append(ELog.Warning, () =>
FLogger.Text("Additive animations have their reference pose stripped, which will lead to inaccurate preview and export", Constants.WHITE, true));
}
_cvaVerifDone = true;
});
}
private int _vfcCount { get; set; }
public void VerifyVirtualCache()
public Task VerifyVirtualCache()
{
if (_vfcCount > 0) return;
_vfcCount = Provider.LoadVirtualCache();
if (_vfcCount > 0)
FLogger.Append(ELog.Information,
() => FLogger.Text($"{_vfcCount} cached packages loaded", Constants.WHITE, true));
return Task.CompletedTask;
return Task.Run(() =>
{
_vfcCount = Provider.LoadVirtualCache();
if (_vfcCount > 0)
FLogger.Append(ELog.Information,
() => FLogger.Text($"{_vfcCount} cached packages loaded", Constants.WHITE, true));
});
}
public void VerifyContentBuildManifest()
public Task VerifyContentBuildManifest()
{
if (!Provider.GameName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase)) return;
if (!Provider.GameName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase))
return Task.CompletedTask;
var persistentDownloadDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FortniteGame/Saved/PersistentDownloadDir");
if (!Directory.Exists(persistentDownloadDir)) return;
var cachedManifest = new DirectoryInfo(Path.Combine(persistentDownloadDir, "ManifestCache")).GetFiles("*.manifest");
if (cachedManifest.Length <= 0)
return;
var manifest = new Manifest(File.ReadAllBytes(cachedManifest[0].FullName), new ManifestOptions
return Task.Run(() =>
{
ChunkBaseUri = new Uri("http://epicgames-download1.akamaized.net/Builds/Fortnite/Content/CloudDir/ChunksV4/", UriKind.Absolute),
ChunkCacheDirectory = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"))
});
var inst = new List<InstructionToken>();
BuildInfo.FindPropertyInstructions("Content", "Label", inst);
if (inst.Count <= 0) return;
var onDemandFiles = new Dictionary<string, GameFile>();
foreach (var fileManifest in manifest.FileManifests)
{
if (Provider.Files.TryGetValue(fileManifest.Name, out _)) continue;
var manifestInfo = _apiEndpointView.EpicApi.GetContentBuildManifest(default, inst[0].Value);
var manifestDir = new DirectoryInfo(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"FortniteGame/Saved/PersistentDownloadDir/ManifestCache"));
var manifestPath = Path.Combine(manifestDir.FullName, manifestInfo?.FileName ?? "");
var onDemandFile = new StreamedGameFile(fileManifest.Name, fileManifest.GetStream(), Provider.Versions);
if (Provider.IsCaseInsensitive) onDemandFiles[onDemandFile.Path.ToLowerInvariant()] = onDemandFile;
else onDemandFiles[onDemandFile.Path] = onDemandFile;
}
byte[] manifestData;
if (File.Exists(manifestPath))
{
manifestData = File.ReadAllBytes(manifestPath);
}
else if (manifestInfo != null)
{
manifestData = manifestInfo.DownloadManifestData();
File.WriteAllBytes(manifestPath, manifestData);
}
else if (manifestDir.Exists && manifestDir.GetFiles("*.manifest") is { Length: > 0} cachedManifests)
{
manifestData = File.ReadAllBytes(cachedManifests[0].FullName);
}
else return;
(Provider.Files as FileProviderDictionary)?.AddFiles(onDemandFiles);
if (onDemandFiles.Count > 0)
FLogger.Append(ELog.Information,
() => FLogger.Text($"{onDemandFiles.Count} streamed packages loaded", Constants.WHITE, true));
#if DEBUG
var manifest = new Manifest(manifestData, new ManifestOptions
{
ChunkBaseUri = new Uri("http://epicgames-download1.akamaized.net/Builds/Fortnite/Content/CloudDir/ChunksV4/", UriKind.Absolute),
ChunkCacheDirectory = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"))
});
var missing = manifest.FileManifests.Count - onDemandFiles.Count;
if (missing != _vfcCount) // false positive if Provider.LoadVirtualCache takes too much time???
FLogger.Append(ELog.Warning,
() => FLogger.Text($"{missing} packages went missing while loading VFC & CBM", Constants.WHITE, true));
#endif
var onDemandFiles = new Dictionary<string, GameFile>();
foreach (var fileManifest in manifest.FileManifests)
{
if (Provider.Files.TryGetValue(fileManifest.Name, out _)) continue;
var onDemandFile = new StreamedGameFile(fileManifest.Name, fileManifest.GetStream(), Provider.Versions);
if (Provider.IsCaseInsensitive) onDemandFiles[onDemandFile.Path.ToLowerInvariant()] = onDemandFile;
else onDemandFiles[onDemandFile.Path] = onDemandFile;
}
(Provider.Files as FileProviderDictionary)?.AddFiles(onDemandFiles);
if (onDemandFiles.Count > 0)
FLogger.Append(ELog.Information,
() => FLogger.Text($"{onDemandFiles.Count} streamed packages loaded", Constants.WHITE, true));
#if DEBUG
var missing = manifest.FileManifests.Count - onDemandFiles.Count;
if (missing != _vfcCount) // false positive if Provider.LoadVirtualCache takes too much time???
FLogger.Append(ELog.Warning,
() => FLogger.Text($"{missing} packages went missing while loading VFC & CBM", Constants.WHITE, true));
#endif
});
}
public int LocalizedResourcesCount { get; set; }
public bool LocalResourcesDone { get; set; }
public bool HotfixedResourcesDone { get; set; }
public async Task LoadLocalizedResources()
{
var snapshot = LocalizedResourcesCount;
await Task.WhenAll(LoadGameLocalizedResources(), LoadHotfixedLocalizedResources()).ConfigureAwait(false);
if (LocalizedResourcesCount > 0)
if (snapshot != LocalizedResourcesCount)
{
FLogger.Append(ELog.Information, () =>
FLogger.Text($"{LocalizedResourcesCount} localized resources loaded for '{UserSettings.Default.AssetLanguage.GetDescription()}'", Constants.WHITE, true));
}
else
{
FLogger.Append(ELog.Warning, () =>
FLogger.Text($"Could not load localized resources in '{UserSettings.Default.AssetLanguage.GetDescription()}', language may not exist", Constants.WHITE, true));
}
}
private bool _localResourcesDone { get; set; }
private Task LoadGameLocalizedResources()
{
if (_localResourcesDone) return Task.CompletedTask;
if (LocalResourcesDone) return Task.CompletedTask;
return Task.Run(() =>
{
LocalizedResourcesCount += Provider.LoadLocalization(UserSettings.Default.AssetLanguage);
_localResourcesDone = true;
LocalResourcesDone = true;
});
}
private bool _hotfixedResourcesDone { get; set; }
private Task LoadHotfixedLocalizedResources()
{
if (Game != FGame.FortniteGame || _hotfixedResourcesDone) return Task.CompletedTask;
if (!Provider.GameName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask;
return Task.Run(() =>
{
var hotfixes = ApplicationService.ApiEndpointView.CentralApi.GetHotfixes(default, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage));
if (hotfixes == null) return;
_hotfixedResourcesDone = true;
HotfixedResourcesDone = true;
foreach (var entries in hotfixes)
{
if (!Provider.LocalizedResources.ContainsKey(entries.Key))

View File

@ -49,6 +49,8 @@ public partial class SettingsView
{
case SettingsOut.ReloadLocres:
_applicationView.CUE4Parse.LocalizedResourcesCount = 0;
_applicationView.CUE4Parse.LocalResourcesDone = false;
_applicationView.CUE4Parse.HotfixedResourcesDone = false;
await _applicationView.CUE4Parse.LoadLocalizedResources();
break;
case SettingsOut.ReloadMappings: