From 3bc1d109881e858623de32025cc4cec7867316e8 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sun, 24 Jul 2022 20:55:53 +0200 Subject: [PATCH 001/178] grrrr --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4c543700..674e3772 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,7 +30,7 @@ jobs: run: dotnet restore FModel - name: .NET Publish - run: dotnet publish FModel -c Release --no-self-contained -r win-x64 -f net7.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }} + run: dotnet publish FModel -c Release --no-self-contained -r win-x64 -f net6.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }} - name: ZIP File uses: papeloto/action-zip@v1 From ffedb2be901cf9ecd4dbf9ee9d42b698710484f2 Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 26 Jul 2022 18:47:12 +0200 Subject: [PATCH 002/178] I feel a little scammed --- .github/workflows/autoclose.yml | 16 ++++++++++++++++ FModel/Framework/FRestRequest.cs | 19 +++++++++++++++++++ .../ApiEndpoints/BenbotApiEndpoint.cs | 9 +++++---- .../ApiEndpoints/EpicApiEndpoint.cs | 5 +++-- FModel/ViewModels/ApiEndpoints/FModelApi.cs | 13 +++++++------ .../ApiEndpoints/FortniteApiEndpoint.cs | 5 +++-- .../ApiEndpoints/ValorantApiEndpoint.cs | 3 ++- 7 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/autoclose.yml create mode 100644 FModel/Framework/FRestRequest.cs diff --git a/.github/workflows/autoclose.yml b/.github/workflows/autoclose.yml new file mode 100644 index 00000000..6c9c7413 --- /dev/null +++ b/.github/workflows/autoclose.yml @@ -0,0 +1,16 @@ +name: Issue Auto Closing + +on: + issues: + types: + - opened + +jobs: + autoclose: + runs-on: ubuntu-latest + steps: + - name: Close Condition + if: contains(toJson(github.event.issue.body), '### Game\r\n\r\nFortnite') + uses: peter-evans/close-issue@v2 + with: + comment: Fortnite related issues are not allowed here. Join the [discord server](https://fmodel.app/discord) and read the common errors channel instead. diff --git a/FModel/Framework/FRestRequest.cs b/FModel/Framework/FRestRequest.cs new file mode 100644 index 00000000..549a00e3 --- /dev/null +++ b/FModel/Framework/FRestRequest.cs @@ -0,0 +1,19 @@ +using System; +using RestSharp; + +namespace FModel.Framework; + +public class FRestRequest : RestRequest +{ + private const int _timeout = 3 * 1000; + + public FRestRequest(string url, Method method = Method.Get) : base(url, method) + { + Timeout = _timeout; + } + + public FRestRequest(Uri uri, Method method = Method.Get) : base(uri, method) + { + Timeout = _timeout; + } +} diff --git a/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs index 9c62a522..ddfb971c 100644 --- a/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using FModel.Framework; using FModel.ViewModels.ApiEndpoints.Models; using RestSharp; using Serilog; @@ -16,7 +17,7 @@ public class BenbotApiEndpoint : AbstractApiProvider public async Task GetAesKeysAsync(CancellationToken token) { - var request = new RestRequest("https://benbot.app/api/v2/aes") + var request = new FRestRequest("https://benbot.app/api/v2/aes") { OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } }; @@ -32,7 +33,7 @@ public class BenbotApiEndpoint : AbstractApiProvider public async Task GetMappingsAsync(CancellationToken token) { - var request = new RestRequest("https://benbot.app/api/v1/mappings") + var request = new FRestRequest("https://benbot.app/api/v1/mappings") { OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } }; @@ -48,7 +49,7 @@ public class BenbotApiEndpoint : AbstractApiProvider public async Task>> GetHotfixesAsync(CancellationToken token, string language = "en-US") { - var request = new RestRequest("https://benbot.app/api/v1/hotfixes") + var request = new FRestRequest("https://benbot.app/api/v1/hotfixes") { OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } }; @@ -65,7 +66,7 @@ public class BenbotApiEndpoint : AbstractApiProvider public async Task DownloadFileAsync(string fileLink, string installationPath) { - var request = new RestRequest(fileLink); + var request = new FRestRequest(fileLink); var data = _client.DownloadData(request); await File.WriteAllBytesAsync(installationPath, data); } diff --git a/FModel/ViewModels/ApiEndpoints/EpicApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/EpicApiEndpoint.cs index 97277d6c..4c391f7e 100644 --- a/FModel/ViewModels/ApiEndpoints/EpicApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/EpicApiEndpoint.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using EpicManifestParser.Objects; +using FModel.Framework; using FModel.Settings; using FModel.ViewModels.ApiEndpoints.Models; using RestSharp; @@ -30,7 +31,7 @@ public class EpicApiEndpoint : AbstractApiProvider } } - var request = new RestRequest(_LAUNCHER_ASSETS); + var request = new FRestRequest(_LAUNCHER_ASSETS); 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); @@ -44,7 +45,7 @@ public class EpicApiEndpoint : AbstractApiProvider private async Task GetAuthAsync(CancellationToken token) { - var request = new RestRequest(_OAUTH_URL, Method.Post); + var request = new FRestRequest(_OAUTH_URL, Method.Post); request.AddHeader("Authorization", _BASIC_TOKEN); request.AddParameter("grant_type", "client_credentials"); var response = await _client.ExecuteAsync(request, token).ConfigureAwait(false); diff --git a/FModel/ViewModels/ApiEndpoints/FModelApi.cs b/FModel/ViewModels/ApiEndpoints/FModelApi.cs index a488b030..b0b5c2ba 100644 --- a/FModel/ViewModels/ApiEndpoints/FModelApi.cs +++ b/FModel/ViewModels/ApiEndpoints/FModelApi.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using System.Windows; using AutoUpdaterDotNET; using FModel.Extensions; +using FModel.Framework; using FModel.Services; using FModel.Settings; using FModel.ViewModels.ApiEndpoints.Models; @@ -34,7 +35,7 @@ public class FModelApi : AbstractApiProvider public async Task GetNewsAsync(CancellationToken token, string game) { - var request = new RestRequest($"https://api.fmodel.app/v1/news/{Constants.APP_VERSION}"); + var request = new FRestRequest($"https://api.fmodel.app/v1/news/{Constants.APP_VERSION}"); request.AddParameter("game", game); 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); @@ -48,7 +49,7 @@ public class FModelApi : AbstractApiProvider public async Task GetInfosAsync(CancellationToken token, EUpdateMode updateMode) { - var request = new RestRequest($"https://api.fmodel.app/v1/infos/{updateMode}"); + var request = new FRestRequest($"https://api.fmodel.app/v1/infos/{updateMode}"); 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.Data; @@ -61,7 +62,7 @@ public class FModelApi : AbstractApiProvider public async Task GetBackupsAsync(CancellationToken token, string gameName) { - var request = new RestRequest($"https://api.fmodel.app/v1/backups/{gameName}"); + var request = new FRestRequest($"https://api.fmodel.app/v1/backups/{gameName}"); 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.Data; @@ -74,7 +75,7 @@ public class FModelApi : AbstractApiProvider public async Task GetGamesAsync(CancellationToken token, string gameName) { - var request = new RestRequest($"https://api.fmodel.app/v1/games/{gameName}"); + var request = new FRestRequest($"https://api.fmodel.app/v1/games/{gameName}"); 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.Data; @@ -87,7 +88,7 @@ public class FModelApi : AbstractApiProvider public async Task GetDesignAsync(string designName) { - var request = new RestRequest($"https://api.fmodel.app/v1/designs/{designName}"); + var request = new FRestRequest($"https://api.fmodel.app/v1/designs/{designName}"); var response = await _client.ExecuteAsync(request).ConfigureAwait(false); Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, response.ResponseUri?.OriginalString); return response.Data != null ? new CommunityDesign(response.Data) : null; @@ -173,7 +174,7 @@ public class FModelApi : AbstractApiProvider private void ShowChangelog(UpdateInfoEventArgs args) { - var request = new RestRequest(args.ChangelogURL); + var request = new FRestRequest(args.ChangelogURL); var response = _client.Execute(request); if (string.IsNullOrEmpty(response.Content)) return; diff --git a/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs index 6878e553..c2036976 100644 --- a/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs @@ -2,6 +2,7 @@ using FModel.ViewModels.ApiEndpoints.Models; using RestSharp; using System.Threading.Tasks; +using FModel.Framework; namespace FModel.ViewModels.ApiEndpoints; @@ -13,7 +14,7 @@ public class FortniteApiEndpoint : AbstractApiProvider public async Task GetPlaylistAsync(string playlistId) { - var request = new RestRequest($"https://fortnite-api.com/v1/playlists/{playlistId}"); + var request = new FRestRequest($"https://fortnite-api.com/v1/playlists/{playlistId}"); var response = await _client.ExecuteAsync(request).ConfigureAwait(false); return response.Data; } @@ -25,7 +26,7 @@ public class FortniteApiEndpoint : AbstractApiProvider public bool TryGetBytes(Uri link, out byte[] data) { - var request = new RestRequest(link); + var request = new FRestRequest(link); data = _client.DownloadData(request); return data != null; } diff --git a/FModel/ViewModels/ApiEndpoints/ValorantApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/ValorantApiEndpoint.cs index 29e23594..8c98133a 100644 --- a/FModel/ViewModels/ApiEndpoints/ValorantApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/ValorantApiEndpoint.cs @@ -13,6 +13,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; +using FModel.Framework; namespace FModel.ViewModels.ApiEndpoints; @@ -26,7 +27,7 @@ public class ValorantApiEndpoint : AbstractApiProvider public async Task GetManifestAsync(CancellationToken token) { - var request = new RestRequest(_URL); + var request = new FRestRequest(_URL); var response = await _client.ExecuteAsync(request, token).ConfigureAwait(false); return new VManifest(response.RawBytes); } From 88383c30f02be7a788840d02b2940978f587945e Mon Sep 17 00:00:00 2001 From: GMatrixGames Date: Wed, 27 Jul 2022 22:15:55 -0400 Subject: [PATCH 003/178] Move mappings and AES to Fortnite Central --- FModel/ViewModels/AesManagerViewModel.cs | 4 +- FModel/ViewModels/ApiEndpointViewModel.cs | 2 + .../ApiEndpoints/BenbotApiEndpoint.cs | 32 ------------- .../FortniteCentralApiEndpoint.cs | 47 +++++++++++++++++++ .../ApiEndpoints/Models/AesResponse.cs | 4 +- FModel/ViewModels/CUE4ParseViewModel.cs | 4 +- 6 files changed, 55 insertions(+), 38 deletions(-) create mode 100644 FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs diff --git a/FModel/ViewModels/AesManagerViewModel.cs b/FModel/ViewModels/AesManagerViewModel.cs index e1677b1f..3b21c3dc 100644 --- a/FModel/ViewModels/AesManagerViewModel.cs +++ b/FModel/ViewModels/AesManagerViewModel.cs @@ -79,7 +79,7 @@ public class AesManagerViewModel : ViewModel new() { Key = key, - FileName = collection[e.CollectionIndex].Name, + Name = collection[e.CollectionIndex].Name, Guid = collection[e.CollectionIndex].Guid.ToString() } }; @@ -97,7 +97,7 @@ public class AesManagerViewModel : ViewModel _keysFromSettings.DynamicKeys.Add(new DynamicKey { Key = key, - FileName = collection[e.CollectionIndex].Name, + Name = collection[e.CollectionIndex].Name, Guid = collection[e.CollectionIndex].Guid.ToString() }); } diff --git a/FModel/ViewModels/ApiEndpointViewModel.cs b/FModel/ViewModels/ApiEndpointViewModel.cs index 55d5d6af..a5c8a047 100644 --- a/FModel/ViewModels/ApiEndpointViewModel.cs +++ b/FModel/ViewModels/ApiEndpointViewModel.cs @@ -17,6 +17,7 @@ public class ApiEndpointViewModel public FortniteApiEndpoint FortniteApi { get; } public ValorantApiEndpoint ValorantApi { get; } + public FortniteCentralApiEndpoint CentralApi { get; } public BenbotApiEndpoint BenbotApi { get; } public EpicApiEndpoint EpicApi { get; } public FModelApi FModelApi { get; } @@ -25,6 +26,7 @@ public class ApiEndpointViewModel { FortniteApi = new FortniteApiEndpoint(_client); ValorantApi = new ValorantApiEndpoint(_client); + CentralApi = new FortniteCentralApiEndpoint(_client); BenbotApi = new BenbotApiEndpoint(_client); EpicApi = new EpicApiEndpoint(_client); FModelApi = new FModelApi(_client); diff --git a/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs index ddfb971c..dbbe7886 100644 --- a/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs @@ -15,38 +15,6 @@ public class BenbotApiEndpoint : AbstractApiProvider { } - public async Task GetAesKeysAsync(CancellationToken token) - { - var request = new FRestRequest("https://benbot.app/api/v2/aes") - { - OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } - }; - 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.Data; - } - - public AesResponse GetAesKeys(CancellationToken token) - { - return GetAesKeysAsync(token).GetAwaiter().GetResult(); - } - - public async Task GetMappingsAsync(CancellationToken token) - { - var request = new FRestRequest("https://benbot.app/api/v1/mappings") - { - OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } - }; - 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.Data; - } - - public MappingsResponse[] GetMappings(CancellationToken token) - { - return GetMappingsAsync(token).GetAwaiter().GetResult(); - } - public async Task>> GetHotfixesAsync(CancellationToken token, string language = "en-US") { var request = new FRestRequest("https://benbot.app/api/v1/hotfixes") diff --git a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs new file mode 100644 index 00000000..c13d8578 --- /dev/null +++ b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs @@ -0,0 +1,47 @@ +using System.Threading; +using System.Threading.Tasks; +using FModel.Framework; +using FModel.ViewModels.ApiEndpoints.Models; +using RestSharp; +using Serilog; + +namespace FModel.ViewModels.ApiEndpoints; + +public class FortniteCentralApiEndpoint : AbstractApiProvider +{ + public FortniteCentralApiEndpoint(RestClient client) : base(client) + { + } + + public async Task GetAesKeysAsync(CancellationToken token) + { + var request = new FRestRequest("https://fortnitecentral.gmatrixgames.ga/api/v1/aes") + { + OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } + }; + 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.Data; + } + + public AesResponse GetAesKeys(CancellationToken token) + { + return GetAesKeysAsync(token).GetAwaiter().GetResult(); + } + + public async Task GetMappingsAsync(CancellationToken token) + { + var request = new FRestRequest("https://fortnitecentral.gmatrixgames.ga/api/v1/mappings") + { + OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } + }; + 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.Data; + } + + public MappingsResponse[] GetMappings(CancellationToken token) + { + return GetMappingsAsync(token).GetAwaiter().GetResult(); + } +} diff --git a/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs b/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs index 1dd3f68e..76e775a9 100644 --- a/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs +++ b/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs @@ -17,7 +17,7 @@ public class AesResponse [DebuggerDisplay("{" + nameof(Key) + "}")] public class DynamicKey { - [J("fileName")] public string FileName { get; set; } + [J("name")] public string Name { get; set; } [J("guid")] public string Guid { get; set; } [J("key")] public string Key { get; set; } -} \ No newline at end of file +} diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 49e9dfa6..ee5b6294 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -296,7 +296,7 @@ public class CUE4ParseViewModel : ViewModel { await _threadWorkerView.Begin(cancellationToken => { - var aes = _apiEndpointView.BenbotApi.GetAesKeys(cancellationToken); + var aes = _apiEndpointView.CentralApi.GetAesKeys(cancellationToken); if (aes?.MainKey == null && aes?.DynamicKeys == null && aes?.Version == null) return; UserSettings.Default.AesKeys[Game] = aes; @@ -333,7 +333,7 @@ public class CUE4ParseViewModel : ViewModel else { var mappingsFolder = Path.Combine(UserSettings.Default.OutputDirectory, ".data"); - var mappings = _apiEndpointView.BenbotApi.GetMappings(cancellationToken); + var mappings = _apiEndpointView.CentralApi.GetMappings(cancellationToken); if (mappings is { Length: > 0 }) { foreach (var mapping in mappings) From 56dd9633a3ff5e755436f86f3aa630d64e788076 Mon Sep 17 00:00:00 2001 From: GMatrixGames Date: Fri, 29 Jul 2022 08:57:24 -0400 Subject: [PATCH 004/178] Move hotfixes too --- FModel/ViewModels/ApiEndpointViewModel.cs | 19 ++++++-- .../ApiEndpoints/BenbotApiEndpoint.cs | 46 ------------------- .../FortniteCentralApiEndpoint.cs | 20 +++++++- FModel/ViewModels/ApplicationViewModel.cs | 2 +- FModel/ViewModels/BackupManagerViewModel.cs | 4 +- FModel/ViewModels/CUE4ParseViewModel.cs | 4 +- 6 files changed, 40 insertions(+), 55 deletions(-) delete mode 100644 FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs diff --git a/FModel/ViewModels/ApiEndpointViewModel.cs b/FModel/ViewModels/ApiEndpointViewModel.cs index a5c8a047..f062266f 100644 --- a/FModel/ViewModels/ApiEndpointViewModel.cs +++ b/FModel/ViewModels/ApiEndpointViewModel.cs @@ -1,4 +1,7 @@ -using FModel.Framework; +using System; +using System.IO; +using System.Threading.Tasks; +using FModel.Framework; using FModel.ViewModels.ApiEndpoints; using RestSharp; @@ -18,7 +21,6 @@ public class ApiEndpointViewModel public FortniteApiEndpoint FortniteApi { get; } public ValorantApiEndpoint ValorantApi { get; } public FortniteCentralApiEndpoint CentralApi { get; } - public BenbotApiEndpoint BenbotApi { get; } public EpicApiEndpoint EpicApi { get; } public FModelApi FModelApi { get; } @@ -27,8 +29,19 @@ public class ApiEndpointViewModel FortniteApi = new FortniteApiEndpoint(_client); ValorantApi = new ValorantApiEndpoint(_client); CentralApi = new FortniteCentralApiEndpoint(_client); - BenbotApi = new BenbotApiEndpoint(_client); EpicApi = new EpicApiEndpoint(_client); FModelApi = new FModelApi(_client); } + + public async Task DownloadFileAsync(string fileLink, string installationPath) + { + var request = new FRestRequest(fileLink); + var data = _client.DownloadData(request) ?? Array.Empty(); + await File.WriteAllBytesAsync(installationPath, data); + } + + public void DownloadFile(string fileLink, string installationPath) + { + DownloadFileAsync(fileLink, installationPath).GetAwaiter().GetResult(); + } } diff --git a/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs deleted file mode 100644 index dbbe7886..00000000 --- a/FModel/ViewModels/ApiEndpoints/BenbotApiEndpoint.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using FModel.Framework; -using FModel.ViewModels.ApiEndpoints.Models; -using RestSharp; -using Serilog; - -namespace FModel.ViewModels.ApiEndpoints; - -public class BenbotApiEndpoint : AbstractApiProvider -{ - public BenbotApiEndpoint(RestClient client) : base(client) - { - } - - public async Task>> GetHotfixesAsync(CancellationToken token, string language = "en-US") - { - var request = new FRestRequest("https://benbot.app/api/v1/hotfixes") - { - OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } - }; - request.AddParameter("lang", language); - 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.Data; - } - - public Dictionary> GetHotfixes(CancellationToken token, string language = "en-US") - { - return GetHotfixesAsync(token, language).GetAwaiter().GetResult(); - } - - public async Task DownloadFileAsync(string fileLink, string installationPath) - { - var request = new FRestRequest(fileLink); - var data = _client.DownloadData(request); - await File.WriteAllBytesAsync(installationPath, data); - } - - public void DownloadFile(string fileLink, string installationPath) - { - DownloadFileAsync(fileLink, installationPath).GetAwaiter().GetResult(); - } -} diff --git a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs index c13d8578..2ebb7d22 100644 --- a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using FModel.Framework; using FModel.ViewModels.ApiEndpoints.Models; @@ -44,4 +45,21 @@ public class FortniteCentralApiEndpoint : AbstractApiProvider { return GetMappingsAsync(token).GetAwaiter().GetResult(); } + + public async Task>> GetHotfixesAsync(CancellationToken token, string language = "en") + { + var request = new FRestRequest("https://fortnitecentral.gmatrixgames.ga/api/v1/hotfixes") + { + OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } + }; + request.AddParameter("lang", language); + 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.Data; + } + + public Dictionary> GetHotfixes(CancellationToken token, string language = "en") + { + return GetHotfixesAsync(token, language).GetAwaiter().GetResult(); + } } diff --git a/FModel/ViewModels/ApplicationViewModel.cs b/FModel/ViewModels/ApplicationViewModel.cs index 75984606..9564f56c 100644 --- a/FModel/ViewModels/ApplicationViewModel.cs +++ b/FModel/ViewModels/ApplicationViewModel.cs @@ -160,7 +160,7 @@ public class ApplicationViewModel : ViewModel var vgmZipFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-win.zip"); if (File.Exists(vgmZipFilePath)) return; - await ApplicationService.ApiEndpointView.BenbotApi.DownloadFileAsync("https://github.com/vgmstream/vgmstream/releases/latest/download/vgmstream-win.zip", vgmZipFilePath); + await ApplicationService.ApiEndpointView.DownloadFileAsync("https://github.com/vgmstream/vgmstream/releases/latest/download/vgmstream-win.zip", vgmZipFilePath); if (new FileInfo(vgmZipFilePath).Length > 0) { var zip = ZipFile.Read(vgmZipFilePath); diff --git a/FModel/ViewModels/BackupManagerViewModel.cs b/FModel/ViewModels/BackupManagerViewModel.cs index 78be7f6b..bef4da3b 100644 --- a/FModel/ViewModels/BackupManagerViewModel.cs +++ b/FModel/ViewModels/BackupManagerViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; @@ -93,7 +93,7 @@ public class BackupManagerViewModel : ViewModel await _threadWorkerView.Begin(_ => { var fullPath = Path.Combine(Path.Combine(UserSettings.Default.OutputDirectory, "Backups"), SelectedBackup.FileName); - _apiEndpointView.BenbotApi.DownloadFile(SelectedBackup.DownloadUrl, fullPath); + _apiEndpointView.DownloadFile(SelectedBackup.DownloadUrl, fullPath); SaveCheck(fullPath, SelectedBackup.FileName, "downloaded", "download"); }); } diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index ee5b6294..acb1fb3c 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -343,7 +343,7 @@ public class CUE4ParseViewModel : ViewModel var mappingPath = Path.Combine(mappingsFolder, mapping.FileName); if (!File.Exists(mappingPath)) { - _apiEndpointView.BenbotApi.DownloadFile(mapping.Url, mappingPath); + _apiEndpointView.DownloadFile(mapping.Url, mappingPath); } Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(mappingPath); @@ -401,7 +401,7 @@ public class CUE4ParseViewModel : ViewModel if (Game != FGame.FortniteGame || HotfixedResourcesDone) return; await _threadWorkerView.Begin(cancellationToken => { - var hotfixes = ApplicationService.ApiEndpointView.BenbotApi.GetHotfixes(cancellationToken, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); + var hotfixes = ApplicationService.ApiEndpointView.CentralApi.GetHotfixes(cancellationToken, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); if (hotfixes == null) return; HotfixedResourcesDone = true; From f429e7c91143d090ebb1f8b100bdf93b90602908 Mon Sep 17 00:00:00 2001 From: shade Date: Fri, 29 Jul 2022 15:31:52 -0400 Subject: [PATCH 005/178] multiversus --- FModel/Creator/Bases/FN/BaseCommunity.cs | 2 +- FModel/Creator/Bases/FN/BaseIcon.cs | 2 +- .../Creator/Bases/MV/BaseMultiversusIcon.cs | 122 ++++++++++++++++++ FModel/Creator/Bases/UCreator.cs | 8 +- FModel/Creator/CreatorPackage.cs | 8 +- FModel/Creator/Typefaces.cs | 27 +++- FModel/Creator/Utils.cs | 3 +- FModel/Enums.cs | 8 +- FModel/FModel.csproj | 2 +- FModel/Settings/UserSettings.cs | 18 ++- 10 files changed, 182 insertions(+), 18 deletions(-) create mode 100644 FModel/Creator/Bases/MV/BaseMultiversusIcon.cs diff --git a/FModel/Creator/Bases/FN/BaseCommunity.cs b/FModel/Creator/Bases/FN/BaseCommunity.cs index 8daf66a4..1e0a7294 100644 --- a/FModel/Creator/Bases/FN/BaseCommunity.cs +++ b/FModel/Creator/Bases/FN/BaseCommunity.cs @@ -272,4 +272,4 @@ public class BaseCommunity : BaseIcon // draw } } -} \ No newline at end of file +} diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index dd00cb57..048e6a0e 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -281,4 +281,4 @@ public class BaseIcon : UCreator x += size; } } -} \ No newline at end of file +} diff --git a/FModel/Creator/Bases/MV/BaseMultiversusIcon.cs b/FModel/Creator/Bases/MV/BaseMultiversusIcon.cs new file mode 100644 index 00000000..a25c044b --- /dev/null +++ b/FModel/Creator/Bases/MV/BaseMultiversusIcon.cs @@ -0,0 +1,122 @@ +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Objects.Core.i18N; +using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.UObject; +using FModel.Creator.Bases.FN; +using SkiaSharp; + +namespace FModel.Creator.Bases.MV; + +public class BaseMultiversusIcon : BaseIcon +{ + public BaseMultiversusIcon(UObject uObject, EIconStyle style) : base(uObject, style) + { + } + + public override void ParseForInfo() + { + if (Object.TryGetValue(out var backgroundColor, "BackgroundColor")) + { + var bgColor = SKColor.Parse(backgroundColor.Hex); + Background = new[] { bgColor, bgColor }; + } + else if (Object.TryGetValue(out var rarity, "Rarity")) + { + Background = GetRarityBackground(rarity.ToString()); + Border = new[] { GetRarityBorder(rarity.ToString()) }; + } + + if (Object.TryGetValue(out var nameColor, "DisplayNameColor")) + { + Border = new[] { SKColor.Parse(nameColor.Hex) }; + } + + if (Object.TryGetValue(out var rewardThumbnail, "RewardThumbnail")) + Preview = Utils.GetBitmap(rewardThumbnail); + + else if (Object.TryGetValue(out var portaitPtr, "CollectionsPortraitMaterial", "CharacterSelectPortraitMaterial") + && portaitPtr.TryLoad(out var portait)) + Preview = Utils.GetBitmap(portait); + + else if (Object.TryGetValue(out var skins, "Skins") && + skins[0].TryLoad(out var defaultSkin) && + defaultSkin.TryGetValue(out var skinRewardThumb, "RewardThumbnail")) + Preview = Utils.GetBitmap(skinRewardThumb); + + else if (Object.TryGetValue(out var eogPortrait, "EOGPortrait")) + Preview = Utils.GetBitmap(eogPortrait); + + if (Object.TryGetValue(out var displayName, "DisplayName")) + DisplayName = displayName.Text; + + if (Object.TryGetValue(out var description, "Overview", "Bio")) + Description = description.Text; + + if (Object.TryGetValue(out var property, "Property")) + ShortDescription = property.Text; + + if (Object.TryGetValue(out var unlockLocation, "UnlockLocation")) + CosmeticSource = unlockLocation.ToString().Split("::")[1]; + } + + private static SKColor[] GetRarityBackground(string rarity) + { + string rarityName = rarity.Split("::")[1]; + + switch (rarityName) // the colors here are the base color and brighter color that the game uses for rarities from the "Rarity to Color" blueprint function + { + case "Common": + return new[] + { + SKColor.Parse(new FLinearColor(0.068478f, 0.651406f, 0.016807f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.081422f, 1.000000f, 0.000000f, 1.000000f).Hex) + }; + case "Rare": + return new[] + { + SKColor.Parse(new FLinearColor(0.035911f, 0.394246f, 0.900000f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.033333f, 0.434207f, 1.000000f, 1.000000f).Hex) + }; + case "Epic": + return new[] + { + SKColor.Parse(new FLinearColor(0.530391f, 0.060502f, 0.900000f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.579907f, 0.045833f, 1.000000f, 1.000000f).Hex) + }; + case "Legendary": + return new[] + { + SKColor.Parse(new FLinearColor(1.000000f, 0.223228f, 0.002428f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(1.000000f, 0.479320f, 0.030713f, 1.000000f).Hex) + }; + case "None": + default: + return new[] + { + SKColor.Parse(new FLinearColor(0.194618f, 0.651406f, 0.630757f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.273627f, 0.955208f, 0.914839f, 1.000000f).Hex) + }; + } + } + + private static SKColor GetRarityBorder(string rarity) + { + string rarityName = rarity.Split("::")[1]; + + switch (rarityName) // the colors here are the desaturated versions of the rarity colors + { + case "Common": + return SKColor.Parse(new FLinearColor(0.172713f, 0.651406f, 0.130281f, 1.000000f).Hex); + case "Rare": + return SKColor.Parse(new FLinearColor(0.198220f, 0.527026f, 0.991102f, 1.000000f).Hex); + case "Epic": + return SKColor.Parse(new FLinearColor(0.642017f, 0.198220f, 0.991102f, 1.000000f).Hex); + case "Legendary": + return SKColor.Parse(new FLinearColor(1.000000f, 0.328434f, 0.200000f, 1.000000f).Hex); + case "None": + default: + return SKColor.Parse(new FLinearColor(0.308843f, 0.571125f, 0.557810f, 1.000000f).Hex); + } + } +} diff --git a/FModel/Creator/Bases/UCreator.cs b/FModel/Creator/Bases/UCreator.cs index d0577b37..7dbceba6 100644 --- a/FModel/Creator/Bases/UCreator.cs +++ b/FModel/Creator/Bases/UCreator.cs @@ -43,13 +43,13 @@ public abstract class UCreator protected readonly SKPaint DisplayNamePaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, - Typeface = Utils.Typefaces.DisplayName, TextSize = _NAME_TEXT_SIZE, + Typeface = Utils.Typefaces.DisplayName ?? Utils.Typefaces.Default, TextSize = _NAME_TEXT_SIZE, Color = SKColors.White, TextAlign = SKTextAlign.Center }; protected readonly SKPaint DescriptionPaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, - Typeface = Utils.Typefaces.Description, TextSize = 13, + Typeface = Utils.Typefaces.Description ?? Utils.Typefaces.Default, TextSize = 13, Color = SKColors.White }; protected readonly SKPaint ImagePaint = new() @@ -214,7 +214,7 @@ public abstract class UCreator switch (side) { case SKTextAlign.Left: - _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName; + _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName ?? Utils.Typefaces.Default; var shaper = new CustomSKShaper(_shortDescriptionPaint.Typeface); shaper.Shape(text, _shortDescriptionPaint); @@ -226,4 +226,4 @@ public abstract class UCreator break; } } -} \ No newline at end of file +} diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 22a842e7..59067ed6 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -4,6 +4,7 @@ using CUE4Parse.UE4.Assets.Exports; using FModel.Creator.Bases; using FModel.Creator.Bases.BB; using FModel.Creator.Bases.FN; +using FModel.Creator.Bases.MV; using FModel.Creator.Bases.SB; namespace FModel.Creator; @@ -173,6 +174,11 @@ public class CreatorPackage : IDisposable case "PlaylistUserOptionCollisionProfileEnum": creator = new BaseUserControl(_object, _style); return true; + // Multiversus + case "CharacterData": + case "SkinData": + creator = new BaseMultiversusIcon(_object, _style); + return true; // Battle Breakers case "WExpGenericAccountItemDefinition": case "WExpGearAccountItemDefinition": @@ -248,4 +254,4 @@ public class CreatorPackage : IDisposable { _object = null; } -} \ No newline at end of file +} diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs index 2b84b600..cdeb696e 100644 --- a/FModel/Creator/Typefaces.cs +++ b/FModel/Creator/Typefaces.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Windows; using CUE4Parse.UE4.Versions; @@ -41,6 +41,11 @@ public class Typefaces private const string _BURBANK_SMALL_BLACK = "burbanksmall-black"; private const string _BURBANK_SMALL_BOLD = "burbanksmall-bold"; + // Multiversus + private const string _MULTIVERSUS_BASE_PATH = "/Game/Panda_Main/UI/Fonts/"; + private const string _MONTSERRAT_BOLD = "Montserrat/Montserrat-Bold"; + private const string _VCCARDINAL_CONDENSED_BOLDITALIC_ITALIC = "VCCardinal/VCCardinalCondensed-BoldItalic"; + // Spellbreak private const string _SPELLBREAK_BASE_PATH = "/Game/UI/Fonts/"; private const string _MONTSERRAT_SEMIBOLD = "Montserrat-Semibold"; @@ -264,6 +269,24 @@ public class Typefaces } else Description = Default; + break; + } + case FGame.Multiversus: + { + if (viewModel.Provider.TrySaveAsset(_MULTIVERSUS_BASE_PATH + _VCCARDINAL_CONDENSED_BOLDITALIC_ITALIC + _EXT, out data)) + { + var m = new MemoryStream(data) { Position = 0 }; + DisplayName = SKTypeface.FromStream(m); + } + else DisplayName = Default; + + if (viewModel.Provider.TrySaveAsset(_MULTIVERSUS_BASE_PATH + _MONTSERRAT_BOLD + _EXT, out data)) + { + var m = new MemoryStream(data) { Position = 0 }; + Description = SKTypeface.FromStream(m); + } + else Description = Default; + break; } } @@ -275,4 +298,4 @@ public class Typefaces var m = new MemoryStream(data) { Position = 0 }; return SKTypeface.FromStream(m); } -} \ No newline at end of file +} diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index 96757c73..73c84c97 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -93,6 +93,7 @@ public static class Utils switch (textureParameter.ParameterInfo.Name.Text) { case "MainTex": + case "Texture": case "TextureA": case "TextureB": case "OfferImage": @@ -391,4 +392,4 @@ public static class Utils return ret; } -} \ No newline at end of file +} diff --git a/FModel/Enums.cs b/FModel/Enums.cs index 04fff3f1..d40cf8a8 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; namespace FModel; @@ -91,7 +91,9 @@ public enum FGame [Description("GTA: The Trilogy - Definitive Edition")] Gameface, [Description("Sea of Thieves")] - Athena + Athena, + [Description("Multiversus")] + Multiversus } public enum ELoadingMode @@ -138,4 +140,4 @@ public enum EIconStyle Cataba, // [Description("Community")] // CommunityMade -} \ No newline at end of file +} diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index d44cd668..5a2ca7dd 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -11,7 +11,7 @@ false true win-x64 - true + false true FModel.App diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 3c1bda62..2d67c5eb 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -286,7 +286,8 @@ namespace FModel.Settings {FGame.TslGame, Constants._NO_PRESET_TRIGGER}, {FGame.PortalWars, Constants._NO_PRESET_TRIGGER}, {FGame.Gameface, Constants._NO_PRESET_TRIGGER}, - {FGame.Athena, Constants._NO_PRESET_TRIGGER} + {FGame.Athena, Constants._NO_PRESET_TRIGGER}, + {FGame.Multiversus, Constants._NO_PRESET_TRIGGER} }; public IDictionary Presets { @@ -314,7 +315,8 @@ namespace FModel.Settings {FGame.TslGame, EGame.GAME_PlayerUnknownsBattlegrounds}, {FGame.PortalWars, EGame.GAME_UE4_LATEST}, {FGame.Gameface, EGame.GAME_GTATheTrilogyDefinitiveEdition}, - {FGame.Athena, EGame.GAME_SeaOfThieves} + {FGame.Athena, EGame.GAME_SeaOfThieves}, + {FGame.Multiversus, EGame.GAME_UE4_LATEST} }; public IDictionary OverridedGame { @@ -342,7 +344,8 @@ namespace FModel.Settings {FGame.TslGame, null}, {FGame.PortalWars, null}, {FGame.Gameface, null}, - {FGame.Athena, null} + {FGame.Athena, null}, + {FGame.Multiversus, null} }; public IDictionary> OverridedCustomVersions { @@ -370,7 +373,8 @@ namespace FModel.Settings {FGame.TslGame, null}, {FGame.PortalWars, null}, {FGame.Gameface, null}, - {FGame.Athena, null} + {FGame.Athena, null}, + {FGame.Multiversus, null} }; public IDictionary> OverridedOptions { @@ -435,6 +439,12 @@ namespace FModel.Settings new("Strings", "g3/Content/Localization/") } }, + { + FGame.Multiversus, new List() + { + new("Characters", "MultiVersus/Content/Panda_Main/Characters/") + } + }, {FGame.StateOfDecay2, new List()}, {FGame.Prospect, new List()}, {FGame.Indiana, new List()}, From ad2f275d5e492448f5d4cd2757ba96d24da542f2 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sun, 31 Jul 2022 03:41:41 +0200 Subject: [PATCH 006/178] don't mind me, simple CTRL S --- CUE4Parse | 2 +- FModel/Creator/Bases/MV/BaseFighter.cs | 109 +++++++ FModel/Creator/Bases/UCreator.cs | 6 +- FModel/Creator/CreatorPackage.cs | 2 + FModel/Creator/Typefaces.cs | 291 +++++++----------- FModel/Creator/Utils.cs | 6 +- FModel/FModel.csproj | 2 +- FModel/Settings/UserSettings.cs | 4 +- FModel/ViewModels/CUE4ParseViewModel.cs | 2 +- FModel/ViewModels/GameSelectorViewModel.cs | 3 +- .../Converters/StringToGameConverter.cs | 3 +- 11 files changed, 233 insertions(+), 197 deletions(-) create mode 100644 FModel/Creator/Bases/MV/BaseFighter.cs diff --git a/CUE4Parse b/CUE4Parse index d1251ca4..403599b4 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit d1251ca4502cc374e65e8bdd6c920fffce20a6f6 +Subproject commit 403599b4939e81cd7b4f3d3b201cf1018e02ece0 diff --git a/FModel/Creator/Bases/MV/BaseFighter.cs b/FModel/Creator/Bases/MV/BaseFighter.cs new file mode 100644 index 00000000..08463856 --- /dev/null +++ b/FModel/Creator/Bases/MV/BaseFighter.cs @@ -0,0 +1,109 @@ +using System; +using System.Linq; +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Objects.Core.i18N; +using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.UObject; +using SkiaSharp; + +namespace FModel.Creator.Bases.MV; + +public class BaseFighter : UCreator +{ + private float _xOffset; + private float _yOffset; + private float _zoom; + private SKBitmap _pattern; + + public BaseFighter(UObject uObject, EIconStyle style) : base(uObject, style) + { + // https://cdn.discordapp.com/attachments/715640455068385422/1003052259917168700/unknown.png + Width = 1024; + DisplayNamePaint.TextAlign = SKTextAlign.Left; + DisplayNamePaint.TextSize = 100; + DefaultPreview = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); + _pattern = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/UI_Textures/halftone_jagged.halftone_jagged"); + } + + public override void ParseForInfo() + { + if (Object.TryGetValue(out FLinearColor backgroundColor, "BackgroundColor")) + Background = new[] { SKColor.Parse(backgroundColor.Hex) }; + + if (Object.TryGetValue(out FSoftObjectPath portraitMaterial, "CollectionsPortraitMaterial") && + portraitMaterial.TryLoad(out UMaterialInstanceConstant portrait)) + { + _xOffset = Math.Abs(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "XOffset")?.ParameterValue ?? 1f); + _yOffset = Math.Abs(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "YOffset")?.ParameterValue / 10 ?? 1f); + _zoom = Math.Clamp(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "Zoom")?.ParameterValue ?? 1f, 0, 1); + Preview = Utils.GetBitmap(portrait); + } + + if (Object.TryGetValue(out FText displayName, "DisplayName")) + DisplayName = displayName.Text; + } + + public override SKBitmap[] Draw() + { + var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul); + using var c = new SKCanvas(ret); + + DrawBackground(c); + DrawPreview(c); + DrawDisplayName(c); + + return new[] { ret }; + } + + private new void DrawBackground(SKCanvas c) + { + c.DrawRect(new SKRect(0, 0, Width, Height), + new SKPaint + { + IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = Background[0] + }); + + if (!string.IsNullOrWhiteSpace(DisplayName)) + { + c.DrawText(DisplayName, -50, 125, new() + { + IsAntialias = true, FilterQuality = SKFilterQuality.High, + Typeface = Utils.Typefaces.DisplayName, TextSize = 200, + TextScaleX = .95f, TextSkewX = -0.25f, Color = SKColors.Black.WithAlpha(25) + }); + } + + c.DrawBitmap(_pattern, new SKRect(0, 256, Width, 512), new SKPaint + { + IsAntialias = true, FilterQuality = SKFilterQuality.High, BlendMode = SKBlendMode.SoftLight + }); + + var path = new SKPath { FillType = SKPathFillType.EvenOdd }; + path.MoveTo(0, 512); + path.LineTo(0, 492); + path.LineTo(Width, 452); + path.LineTo(Width, 512); + path.Close(); + c.DrawPath(path, new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.High, + Color = SKColor.Parse("#141629") + }); + } + + private new void DrawPreview(SKCanvas c) + { + var img = (Preview ?? DefaultPreview).ResizeWithRatio(_zoom); + var x_offset = img.Width * _xOffset; + var y_offset = img.Height * -_yOffset; + c.DrawBitmap(img, new SKRect(Width + x_offset - img.Width, y_offset, Width + x_offset, img.Height + y_offset), ImagePaint); + } + + private new void DrawDisplayName(SKCanvas c) + { + if (string.IsNullOrWhiteSpace(DisplayName)) return; + c.DrawText(DisplayName.ToUpper(), 50, 100, DisplayNamePaint); + } +} diff --git a/FModel/Creator/Bases/UCreator.cs b/FModel/Creator/Bases/UCreator.cs index 7dbceba6..7295c450 100644 --- a/FModel/Creator/Bases/UCreator.cs +++ b/FModel/Creator/Bases/UCreator.cs @@ -43,13 +43,13 @@ public abstract class UCreator protected readonly SKPaint DisplayNamePaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, - Typeface = Utils.Typefaces.DisplayName ?? Utils.Typefaces.Default, TextSize = _NAME_TEXT_SIZE, + Typeface = Utils.Typefaces.DisplayName, TextSize = _NAME_TEXT_SIZE, Color = SKColors.White, TextAlign = SKTextAlign.Center }; protected readonly SKPaint DescriptionPaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, - Typeface = Utils.Typefaces.Description ?? Utils.Typefaces.Default, TextSize = 13, + Typeface = Utils.Typefaces.Description, TextSize = 13, Color = SKColors.White }; protected readonly SKPaint ImagePaint = new() @@ -214,7 +214,7 @@ public abstract class UCreator switch (side) { case SKTextAlign.Left: - _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName ?? Utils.Typefaces.Default; + _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName; var shaper = new CustomSKShaper(_shortDescriptionPaint.Typeface); shaper.Shape(text, _shortDescriptionPaint); diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 59067ed6..4bf2b4e1 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -176,6 +176,8 @@ public class CreatorPackage : IDisposable return true; // Multiversus case "CharacterData": + creator = new BaseFighter(_object, _style); + return true; case "SkinData": creator = new BaseMultiversusIcon(_object, _style); return true; diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs index cdeb696e..9fbbe021 100644 --- a/FModel/Creator/Typefaces.cs +++ b/FModel/Creator/Typefaces.cs @@ -44,7 +44,7 @@ public class Typefaces // Multiversus private const string _MULTIVERSUS_BASE_PATH = "/Game/Panda_Main/UI/Fonts/"; private const string _MONTSERRAT_BOLD = "Montserrat/Montserrat-Bold"; - private const string _VCCARDINAL_CONDENSED_BOLDITALIC_ITALIC = "VCCardinal/VCCardinalCondensed-BoldItalic"; + private const string _NORMS_STD_CONDENSED_BLACK = "Norms/TT_Norms_Std_Condensed_Black"; // Spellbreak private const string _SPELLBREAK_BASE_PATH = "/Game/UI/Fonts/"; @@ -77,224 +77,145 @@ public class Typefaces public Typefaces(CUE4ParseViewModel viewModel) { - byte[] data; _viewModel = viewModel; var language = UserSettings.Default.AssetLanguage; + Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream); switch (viewModel.Game) { case FGame.FortniteGame: { - var namePath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => _ASIA_ERINM, - ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK, - ELanguage.Japanese => _NIS_JYAU, - ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, - ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, - ELanguage.Chinese => _NOTO_SANS_SC_BLACK, - _ => string.Empty - }; - if (viewModel.Provider.TrySaveAsset(namePath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - DisplayName = SKTypeface.FromStream(m); - } - else DisplayName = Default; + DisplayName = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => _ASIA_ERINM, + ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK, + ELanguage.Japanese => _NIS_JYAU, + ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, + ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, + ELanguage.Chinese => _NOTO_SANS_SC_BLACK, + _ => string.Empty + } + _EXT); - var descriptionPath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => _NOTO_SANS_KR_REGULAR, - ELanguage.Japanese => _NOTO_SANS_JP_BOLD, - ELanguage.Arabic => _NOTO_SANS_ARABIC_REGULAR, - ELanguage.TraditionalChinese => _NOTO_SANS_TC_REGULAR, - ELanguage.Chinese => _NOTO_SANS_SC_REGULAR, - _ => _NOTO_SANS_REGULAR - }; - if (viewModel.Provider.TrySaveAsset(descriptionPath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - Description = SKTypeface.FromStream(m); - } - else Description = Default; + Description = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => _NOTO_SANS_KR_REGULAR, + ELanguage.Japanese => _NOTO_SANS_JP_BOLD, + ELanguage.Arabic => _NOTO_SANS_ARABIC_REGULAR, + ELanguage.TraditionalChinese => _NOTO_SANS_TC_REGULAR, + ELanguage.Chinese => _NOTO_SANS_SC_REGULAR, + _ => _NOTO_SANS_REGULAR + } + _EXT); - var bottomPath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => string.Empty, - ELanguage.Japanese => string.Empty, - ELanguage.Arabic => string.Empty, - ELanguage.TraditionalChinese => string.Empty, - ELanguage.Chinese => string.Empty, - _ => _BURBANK_SMALL_BOLD - }; - if (viewModel.Provider.TrySaveAsset(bottomPath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - Bottom = SKTypeface.FromStream(m); - } - // else keep it null + Bottom = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => string.Empty, + ELanguage.Japanese => string.Empty, + ELanguage.Arabic => string.Empty, + ELanguage.TraditionalChinese => string.Empty, + ELanguage.Chinese => string.Empty, + _ => _BURBANK_SMALL_BOLD + } + _EXT, true); - if (viewModel.Provider.TrySaveAsset(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - BundleNumber = SKTypeface.FromStream(m); - } - else BundleNumber = Default; + BundleNumber = OnTheFly(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK + _EXT); - var bundleNamePath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => _ASIA_ERINM, - ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK, - ELanguage.Japanese => _NIS_JYAU, - ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, - ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, - ELanguage.Chinese => _NOTO_SANS_SC_BLACK, - _ => string.Empty - }; - if (viewModel.Provider.TrySaveAsset(bundleNamePath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - Bundle = SKTypeface.FromStream(m); - } - else Bundle = BundleNumber; + Bundle = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => _ASIA_ERINM, + ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK, + ELanguage.Japanese => _NIS_JYAU, + ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, + ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, + ELanguage.Chinese => _NOTO_SANS_SC_BLACK, + _ => string.Empty + } + _EXT, true) ?? BundleNumber; - var tandemDisplayNamePath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => _ASIA_ERINM, - ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK, - ELanguage.Japanese => _NIS_JYAU, - ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, - ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, - ELanguage.Chinese => _NOTO_SANS_SC_BLACK, - _ => _BURBANK_BIG_REGULAR_BLACK - }; - if (viewModel.Provider.TrySaveAsset(tandemDisplayNamePath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - TandemDisplayName = SKTypeface.FromStream(m); - } - else TandemDisplayName = Default; + TandemDisplayName = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => _ASIA_ERINM, + ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK, + ELanguage.Japanese => _NIS_JYAU, + ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, + ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, + ELanguage.Chinese => _NOTO_SANS_SC_BLACK, + _ => _BURBANK_BIG_REGULAR_BLACK + } + _EXT); - var tandemGeneralDescPath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => _ASIA_ERINM, - ELanguage.Japanese => _NIS_JYAU, - ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, - ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, - ELanguage.Chinese => _NOTO_SANS_SC_BLACK, - _ => _BURBANK_SMALL_BLACK - }; - if (viewModel.Provider.TrySaveAsset(tandemGeneralDescPath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - TandemGenDescription = SKTypeface.FromStream(m); - } - else TandemGenDescription = Default; - - var tandemAdditionalDescPath = _FORTNITE_BASE_PATH + - language switch - { - ELanguage.Korean => _ASIA_ERINM, - ELanguage.Japanese => _NIS_JYAU, - ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, - ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, - ELanguage.Chinese => _NOTO_SANS_SC_BLACK, - _ => _BURBANK_SMALL_BOLD - }; - if (viewModel.Provider.TrySaveAsset(tandemAdditionalDescPath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - TandemAddDescription = SKTypeface.FromStream(m); - } - else TandemAddDescription = Default; + TandemGenDescription = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => _ASIA_ERINM, + ELanguage.Japanese => _NIS_JYAU, + ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, + ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, + ELanguage.Chinese => _NOTO_SANS_SC_BLACK, + _ => _BURBANK_SMALL_BLACK + } + _EXT); + TandemAddDescription = OnTheFly(_FORTNITE_BASE_PATH + + language switch + { + ELanguage.Korean => _ASIA_ERINM, + ELanguage.Japanese => _NIS_JYAU, + ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK, + ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK, + ELanguage.Chinese => _NOTO_SANS_SC_BLACK, + _ => _BURBANK_SMALL_BOLD + } + _EXT); break; } case FGame.WorldExplorers: { - var namePath = _BATTLE_BREAKERS_BASE_PATH + - language switch - { - ELanguage.Korean => _NOTO_SANS_KR_REGULAR, - ELanguage.Russian => _LATO_BLACK, - ELanguage.Japanese => _NOTO_SANS_JP_REGULAR, - ELanguage.Chinese => _NOTO_SANS_SC_REGULAR, - _ => _HEMIHEAD426 - }; - if (viewModel.Provider.TrySaveAsset(namePath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - DisplayName = SKTypeface.FromStream(m); - } - else DisplayName = Default; - - var descriptionPath = _BATTLE_BREAKERS_BASE_PATH + - language switch - { - ELanguage.Korean => _NOTO_SANS_KR_REGULAR, - ELanguage.Russian => _LATO_BLACK, - ELanguage.Japanese => _NOTO_SANS_JP_REGULAR, - ELanguage.Chinese => _NOTO_SANS_SC_REGULAR, - _ => _HEMIHEAD426 - }; - if (viewModel.Provider.TrySaveAsset(descriptionPath + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - Description = SKTypeface.FromStream(m); - } - else Description = Default; + DisplayName = OnTheFly(_BATTLE_BREAKERS_BASE_PATH + + language switch + { + ELanguage.Korean => _NOTO_SANS_KR_REGULAR, + ELanguage.Russian => _LATO_BLACK, + ELanguage.Japanese => _NOTO_SANS_JP_REGULAR, + ELanguage.Chinese => _NOTO_SANS_SC_REGULAR, + _ => _HEMIHEAD426 + } + _EXT); + Description = OnTheFly(_BATTLE_BREAKERS_BASE_PATH + + language switch + { + ELanguage.Korean => _NOTO_SANS_KR_REGULAR, + ELanguage.Russian => _LATO_BLACK, + ELanguage.Japanese => _NOTO_SANS_JP_REGULAR, + ELanguage.Chinese => _NOTO_SANS_SC_REGULAR, + _ => _HEMIHEAD426 + } + _EXT); break; } case FGame.g3: { - if (viewModel.Provider.TrySaveAsset(_SPELLBREAK_BASE_PATH + _QUADRAT_BOLD + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - DisplayName = SKTypeface.FromStream(m); - } - else DisplayName = Default; - - if (viewModel.Provider.TrySaveAsset(_SPELLBREAK_BASE_PATH + _MONTSERRAT_SEMIBOLD + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - Description = SKTypeface.FromStream(m); - } - else Description = Default; - + DisplayName = OnTheFly(_SPELLBREAK_BASE_PATH + _QUADRAT_BOLD + _EXT); + Description = OnTheFly(_SPELLBREAK_BASE_PATH + _MONTSERRAT_SEMIBOLD + _EXT); break; } case FGame.Multiversus: { - if (viewModel.Provider.TrySaveAsset(_MULTIVERSUS_BASE_PATH + _VCCARDINAL_CONDENSED_BOLDITALIC_ITALIC + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - DisplayName = SKTypeface.FromStream(m); - } - else DisplayName = Default; - - if (viewModel.Provider.TrySaveAsset(_MULTIVERSUS_BASE_PATH + _MONTSERRAT_BOLD + _EXT, out data)) - { - var m = new MemoryStream(data) { Position = 0 }; - Description = SKTypeface.FromStream(m); - } - else Description = Default; - + DisplayName = OnTheFly(_MULTIVERSUS_BASE_PATH + _NORMS_STD_CONDENSED_BLACK + _EXT); + Description = OnTheFly(_MULTIVERSUS_BASE_PATH + _MONTSERRAT_BOLD + _EXT); + break; + } + default: + { + DisplayName = Default; + Description = Default; break; } } } - public SKTypeface OnTheFly(string path) + public SKTypeface OnTheFly(string path, bool fallback = false) { - if (!_viewModel.Provider.TrySaveAsset(path, out var data)) return Default; + if (!_viewModel.Provider.TrySaveAsset(path, out var data)) return fallback ? null : Default; var m = new MemoryStream(data) { Position = 0 }; return SKTypeface.FromStream(m); } diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index 73c84c97..216d7a02 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -118,12 +118,14 @@ public static class Utils { var ratioX = width / me.Width; var ratioY = height / me.Height; - var ratio = ratioX < ratioY ? ratioX : ratioY; + return ResizeWithRatio(me, ratioX < ratioY ? ratioX : ratioY); + } + public static SKBitmap ResizeWithRatio(this SKBitmap me, double ratio) + { return me.Resize(Convert.ToInt32(me.Width * ratio), Convert.ToInt32(me.Height * ratio)); } public static SKBitmap Resize(this SKBitmap me, int size) => me.Resize(size, size); - public static SKBitmap Resize(this SKBitmap me, int width, int height) { var bmp = new SKBitmap(new SKImageInfo(width, height), SKBitmapAllocFlags.ZeroPixels); diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index 5a2ca7dd..d44cd668 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -11,7 +11,7 @@ false true win-x64 - false + true true FModel.App diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 2d67c5eb..b84b34e5 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -316,7 +316,7 @@ namespace FModel.Settings {FGame.PortalWars, EGame.GAME_UE4_LATEST}, {FGame.Gameface, EGame.GAME_GTATheTrilogyDefinitiveEdition}, {FGame.Athena, EGame.GAME_SeaOfThieves}, - {FGame.Multiversus, EGame.GAME_UE4_LATEST} + {FGame.Multiversus, EGame.GAME_UE4_26} }; public IDictionary OverridedGame { @@ -654,7 +654,7 @@ namespace FModel.Settings } } - private bool _saveAnimations; + private bool _saveAnimations = true; public bool SaveAnimations { get => _saveAnimations; diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index acb1fb3c..ed4ea96c 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -370,6 +370,7 @@ public class CUE4ParseViewModel : ViewModel { await LoadGameLocalizedResources(); await LoadHotfixedLocalizedResources(); + await _threadWorkerView.Begin(_ => Utils.Typefaces = new Typefaces(this)); if (LocalizedResourcesCount > 0) { FLogger.AppendInformation(); @@ -388,7 +389,6 @@ public class CUE4ParseViewModel : ViewModel await _threadWorkerView.Begin(cancellationToken => { LocalizedResourcesCount = Provider.LoadLocalization(UserSettings.Default.AssetLanguage, cancellationToken); - Utils.Typefaces = new Typefaces(this); }); } diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index b8840bde..966c14d1 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -109,6 +109,7 @@ public class GameSelectorViewModel : ViewModel yield return GetUnrealEngineGame("Snoek", "\\StateOfDecay2\\Content\\Paks"); yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508", "\\Core\\Platform\\Content\\Paks"); yield return GetUnrealEngineGame("Nebula", "\\BendGame\\Content"); + yield return GetUnrealEngineGame("711c5e95dc094ca58e5f16bd48e751d6", "\\MultiVersus\\Content"); yield return GetRiotGame("VALORANT", "ShooterGame\\Content\\Paks"); yield return new DetectedGame { GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER }; yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks"); @@ -365,4 +366,4 @@ public class GameSelectorViewModel : ViewModel } } } -} \ No newline at end of file +} diff --git a/FModel/Views/Resources/Converters/StringToGameConverter.cs b/FModel/Views/Resources/Converters/StringToGameConverter.cs index 05fac32f..bc856ef6 100644 --- a/FModel/Views/Resources/Converters/StringToGameConverter.cs +++ b/FModel/Views/Resources/Converters/StringToGameConverter.cs @@ -25,6 +25,7 @@ public class StringToGameConverter : IValueConverter "MinecraftDungeons" => "Minecraft Dungeons", "shoebill" => "Star Wars: Jedi Fallen Order", "a99769d95d8f400baad1f67ab5dfe508" => "Core", + "711c5e95dc094ca58e5f16bd48e751d6" => "MultiVersus", 381210 => "Dead By Daylight", 578080 => "PLAYERUNKNOWN'S BATTLEGROUNDS", 677620 => "Splitgate", @@ -37,4 +38,4 @@ public class StringToGameConverter : IValueConverter { throw new NotImplementedException(); } -} \ No newline at end of file +} From d046e6254ff81c3d5261e10428537ce1d66d160b Mon Sep 17 00:00:00 2001 From: 4sval Date: Sun, 31 Jul 2022 14:29:32 +0200 Subject: [PATCH 007/178] more stuff --- FModel/Creator/Bases/MV/BaseFighter.cs | 45 +++++++++++++++++++++++++- FModel/Creator/Typefaces.cs | 4 +-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/FModel/Creator/Bases/MV/BaseFighter.cs b/FModel/Creator/Bases/MV/BaseFighter.cs index 08463856..2f7a0b3e 100644 --- a/FModel/Creator/Bases/MV/BaseFighter.cs +++ b/FModel/Creator/Bases/MV/BaseFighter.cs @@ -1,6 +1,8 @@ using System; using System.Linq; +using System.Collections.Generic; using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Engine; using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.Core.Math; @@ -14,7 +16,10 @@ public class BaseFighter : UCreator private float _xOffset; private float _yOffset; private float _zoom; - private SKBitmap _pattern; + + private readonly SKBitmap _pattern; + + private (SKBitmap, List) _fighterType; public BaseFighter(UObject uObject, EIconStyle style) : base(uObject, style) { @@ -22,8 +27,11 @@ public class BaseFighter : UCreator Width = 1024; DisplayNamePaint.TextAlign = SKTextAlign.Left; DisplayNamePaint.TextSize = 100; + DescriptionPaint.TextSize = 25; DefaultPreview = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); + _pattern = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/UI_Textures/halftone_jagged.halftone_jagged"); + _fighterType.Item2 = new List(); } public override void ParseForInfo() @@ -42,6 +50,10 @@ public class BaseFighter : UCreator if (Object.TryGetValue(out FText displayName, "DisplayName")) DisplayName = displayName.Text; + + GetFighterClassInfo(Object.GetOrDefault("Class", EFighterClass.Support)); + if (Object.TryGetValue(out FText property, "Property")) + _fighterType.Item2.Add(property.Text); } public override SKBitmap[] Draw() @@ -52,10 +64,25 @@ public class BaseFighter : UCreator DrawBackground(c); DrawPreview(c); DrawDisplayName(c); + DrawFighterInfo(c); return new[] { ret }; } + private void GetFighterClassInfo(EFighterClass clas) + { + if (!Utils.TryLoadObject("MultiVersus/Content/Panda_Main/UI/In-Game/Data/UICharacterClassInfo_Datatable.UICharacterClassInfo_Datatable", out UDataTable dataTable)) + return; + + var row = dataTable.RowMap.ElementAt((int) clas).Value; + if (!row.TryGetValue(out FText displayName, "DisplayName_5_9DB5DDFF490E1F4AD72329866F96B81D") || + !row.TryGetValue(out FPackageIndex icon, "Icon_8_711534AD4F240D4B001AA6A471EA1895")) + return; + + _fighterType.Item1 = Utils.GetBitmap(icon); + _fighterType.Item2.Add(displayName.Text); + } + private new void DrawBackground(SKCanvas c) { c.DrawRect(new SKRect(0, 0, Width, Height), @@ -106,4 +133,20 @@ public class BaseFighter : UCreator if (string.IsNullOrWhiteSpace(DisplayName)) return; c.DrawText(DisplayName.ToUpper(), 50, 100, DisplayNamePaint); } + + private void DrawFighterInfo(SKCanvas c) + { + c.DrawBitmap(_fighterType.Item1, new SKRect(50, 112.5f, 98, 160.5f), ImagePaint); + c.DrawText(string.Join(" | ", _fighterType.Item2), 98, 145, DescriptionPaint); + } +} + +public enum EFighterClass : byte +{ + Mage = 4, + Tank = 3, + Fighter = 2, + Bruiser = 2, + Assassin = 1, + Support = 0 // Default } diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs index 9fbbe021..8527d23e 100644 --- a/FModel/Creator/Typefaces.cs +++ b/FModel/Creator/Typefaces.cs @@ -43,8 +43,8 @@ public class Typefaces // Multiversus private const string _MULTIVERSUS_BASE_PATH = "/Game/Panda_Main/UI/Fonts/"; - private const string _MONTSERRAT_BOLD = "Montserrat/Montserrat-Bold"; private const string _NORMS_STD_CONDENSED_BLACK = "Norms/TT_Norms_Std_Condensed_Black"; + private const string _NORMS_STD_CONDENSED_BOLD = "Norms/TT_Norms_Std_Condensed_Bold"; // Spellbreak private const string _SPELLBREAK_BASE_PATH = "/Game/UI/Fonts/"; @@ -201,7 +201,7 @@ public class Typefaces case FGame.Multiversus: { DisplayName = OnTheFly(_MULTIVERSUS_BASE_PATH + _NORMS_STD_CONDENSED_BLACK + _EXT); - Description = OnTheFly(_MULTIVERSUS_BASE_PATH + _MONTSERRAT_BOLD + _EXT); + Description = OnTheFly(_MULTIVERSUS_BASE_PATH + _NORMS_STD_CONDENSED_BOLD + _EXT); break; } default: From 27791e0202ee99247beb0e98eed176054bbe4bee Mon Sep 17 00:00:00 2001 From: 4sval Date: Sun, 31 Jul 2022 16:07:03 +0200 Subject: [PATCH 008/178] done --- FModel/Creator/Bases/MV/BaseFighter.cs | 112 +++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/FModel/Creator/Bases/MV/BaseFighter.cs b/FModel/Creator/Bases/MV/BaseFighter.cs index 2f7a0b3e..47a51f8d 100644 --- a/FModel/Creator/Bases/MV/BaseFighter.cs +++ b/FModel/Creator/Bases/MV/BaseFighter.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Exports.Engine; using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.UObject; @@ -18,8 +19,14 @@ public class BaseFighter : UCreator private float _zoom; private readonly SKBitmap _pattern; + private readonly SKBitmap _perk; + private readonly SKBitmap _emote; + private readonly SKBitmap _skin; private (SKBitmap, List) _fighterType; + private readonly List _recommendedPerks; + private readonly List _availableTaunts; + private readonly List _skins; public BaseFighter(UObject uObject, EIconStyle style) : base(uObject, style) { @@ -31,7 +38,13 @@ public class BaseFighter : UCreator DefaultPreview = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); _pattern = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/UI_Textures/halftone_jagged.halftone_jagged"); + _perk = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/Icons/ui_icons_perks.ui_icons_perks"); + _emote = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/Icons/ui_icons_emote.ui_icons_emote"); + _skin = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/Icons/ui_icons_skins.ui_icons_skins"); _fighterType.Item2 = new List(); + _recommendedPerks = new List(); + _availableTaunts = new List(); + _skins = new List(); } public override void ParseForInfo() @@ -54,6 +67,43 @@ public class BaseFighter : UCreator GetFighterClassInfo(Object.GetOrDefault("Class", EFighterClass.Support)); if (Object.TryGetValue(out FText property, "Property")) _fighterType.Item2.Add(property.Text); + + if (Object.TryGetValue(out UScriptSet recommendedPerks, "RecommendedPerkDatas")) // PORCO DIO WB USE ARRAYS!!!!!! + { + foreach (var recommendedPerk in recommendedPerks.Properties) + { + if (recommendedPerk.GenericValue is not FPackageIndex packageIndex || + !Utils.TryGetPackageIndexExport(packageIndex, out UObject export) || + !export.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail")) + continue; + + _recommendedPerks.Add(Utils.GetBitmap(rewardThumbnail)); + } + } + + if (Object.TryGetValue(out FSoftObjectPath[] availableTaunts, "AvailableTauntData")) + { + foreach (var taunt in availableTaunts) + { + if (!Utils.TryLoadObject(taunt.AssetPathName.Text, out UObject export) || + !export.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail")) + continue; + + _availableTaunts.Add(Utils.GetBitmap(rewardThumbnail)); + } + } + + if (Object.TryGetValue(out FSoftObjectPath[] skins, "Skins")) + { + foreach (var skin in skins) + { + if (!Utils.TryLoadObject(skin.AssetPathName.Text, out UObject export) || + !export.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail")) + continue; + + _skins.Add(Utils.GetBitmap(rewardThumbnail)); + } + } } public override SKBitmap[] Draw() @@ -65,6 +115,9 @@ public class BaseFighter : UCreator DrawPreview(c); DrawDisplayName(c); DrawFighterInfo(c); + DrawRecommendedPerks(c); + DrawAvailableTaunts(c); + DrawSkins(c); return new[] { ret }; } @@ -139,6 +192,65 @@ public class BaseFighter : UCreator c.DrawBitmap(_fighterType.Item1, new SKRect(50, 112.5f, 98, 160.5f), ImagePaint); c.DrawText(string.Join(" | ", _fighterType.Item2), 98, 145, DescriptionPaint); } + + private void DrawRecommendedPerks(SKCanvas c) + { + const int x = 50; + const int y = 200; + const int size = 64; + + ImagePaint.ImageFilter = null; + ImagePaint.BlendMode = SKBlendMode.SoftLight; + c.DrawBitmap(_perk, new SKRect(x, y, x + size / 2, y + size / 2), ImagePaint); + + ImagePaint.BlendMode = SKBlendMode.SrcOver; + ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 2.5f, 2.5f, SKColors.Black); + c.DrawBitmap(_recommendedPerks[1], new SKRect(161, y, 225, y + size), ImagePaint); + c.DrawBitmap(_recommendedPerks[2], new SKRect(193, y + size / 2, 257, y + size * 1.5f), ImagePaint); + c.DrawBitmap(_recommendedPerks[3], new SKRect(161, y + size, 225, y + size * 2), ImagePaint); + + ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, SKColors.Black.WithAlpha(150)); + c.DrawBitmap(_recommendedPerks[0], new SKRect(x, y, x + size * 2, y + size * 2), ImagePaint); + } + + private void DrawAvailableTaunts(SKCanvas c) + { + var x = 300; + const int y = 232; + const int size = 64; + + ImagePaint.ImageFilter = null; + ImagePaint.BlendMode = SKBlendMode.SoftLight; + c.DrawBitmap(_emote, new SKRect(x, y - size / 2, x + size / 2, y), ImagePaint); + + ImagePaint.BlendMode = SKBlendMode.SrcOver; + ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 1.5f, 1.5f, SKColors.Black); + + foreach (var taunt in _availableTaunts) + { + c.DrawBitmap(taunt, new SKRect(x, y, x + size, y + size), ImagePaint); + x += size; + } + } + + private void DrawSkins(SKCanvas c) + { + var x = 50; + const int y = 333; + const int size = 128; + + ImagePaint.ImageFilter = null; + ImagePaint.BlendMode = SKBlendMode.SoftLight; + c.DrawBitmap(_skin, new SKRect(x, y, x + size / 4, y + size / 4), ImagePaint); + + ImagePaint.BlendMode = SKBlendMode.SrcOver; + ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 1.5f, 1.5f, SKColors.Black); + foreach (var skin in _skins) + { + c.DrawBitmap(skin, new SKRect(x, y, x + size, y + size), ImagePaint); + x += size; + } + } } public enum EFighterClass : byte From 8725cdadf5d3f559d2c9ebae7761310eb94e7bff Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 2 Aug 2022 00:31:43 +0200 Subject: [PATCH 009/178] ctrl s --- FModel/Creator/Bases/MV/BaseFighter.cs | 35 +++++++-- ...tiversusIcon.cs => BaseMultiVersusIcon.cs} | 76 +++++++------------ FModel/Creator/CreatorPackage.cs | 14 +++- FModel/Creator/Typefaces.cs | 14 +++- FModel/MainWindow.xaml.cs | 2 + 5 files changed, 83 insertions(+), 58 deletions(-) rename FModel/Creator/Bases/MV/{BaseMultiversusIcon.cs => BaseMultiVersusIcon.cs} (50%) diff --git a/FModel/Creator/Bases/MV/BaseFighter.cs b/FModel/Creator/Bases/MV/BaseFighter.cs index 47a51f8d..fc5da665 100644 --- a/FModel/Creator/Bases/MV/BaseFighter.cs +++ b/FModel/Creator/Bases/MV/BaseFighter.cs @@ -14,9 +14,9 @@ namespace FModel.Creator.Bases.MV; public class BaseFighter : UCreator { - private float _xOffset; - private float _yOffset; - private float _zoom; + private float _xOffset = 1f; + private float _yOffset = 1f; + private float _zoom = 1f; private readonly SKBitmap _pattern; private readonly SKBitmap _perk; @@ -30,7 +30,6 @@ public class BaseFighter : UCreator public BaseFighter(UObject uObject, EIconStyle style) : base(uObject, style) { - // https://cdn.discordapp.com/attachments/715640455068385422/1003052259917168700/unknown.png Width = 1024; DisplayNamePaint.TextAlign = SKTextAlign.Left; DisplayNamePaint.TextSize = 100; @@ -60,11 +59,14 @@ public class BaseFighter : UCreator _zoom = Math.Clamp(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "Zoom")?.ParameterValue ?? 1f, 0, 1); Preview = Utils.GetBitmap(portrait); } + else if (Object.TryGetValue(out FSoftObjectPath portraitTexture, "NewCharacterSelectPortraitTexture", "HUDPortraitTexture")) + Preview = Utils.GetBitmap(portraitTexture); if (Object.TryGetValue(out FText displayName, "DisplayName")) DisplayName = displayName.Text; GetFighterClassInfo(Object.GetOrDefault("Class", EFighterClass.Support)); + _fighterType.Item2.Add(GetFighterType(Object.GetOrDefault("Type", EFighterType.Horizontal))); if (Object.TryGetValue(out FText property, "Property")) _fighterType.Item2.Add(property.Text); @@ -136,6 +138,17 @@ public class BaseFighter : UCreator _fighterType.Item2.Add(displayName.Text); } + private string GetFighterType(EFighterType typ) + { + return typ switch + { + EFighterType.Horizontal => Utils.GetLocalizedResource("", "97A60DD54AA23D4B93D5B891F729BF5C", "Horizontal"), + EFighterType.Vertical => Utils.GetLocalizedResource("", "2C55443D47164019BE73A5ABDC670F36", "Vertical"), + EFighterType.Hybrid => Utils.GetLocalizedResource("", "B980C82D40FF37FD359C74A339CE1B3A", "Hybrid"), + _ => typ.ToString() + }; + } + private new void DrawBackground(SKCanvas c) { c.DrawRect(new SKRect(0, 0, Width, Height), @@ -189,7 +202,9 @@ public class BaseFighter : UCreator private void DrawFighterInfo(SKCanvas c) { - c.DrawBitmap(_fighterType.Item1, new SKRect(50, 112.5f, 98, 160.5f), ImagePaint); + if (_fighterType.Item1 != null) + c.DrawBitmap(_fighterType.Item1, new SKRect(50, 112.5f, 98, 160.5f), ImagePaint); + c.DrawText(string.Join(" | ", _fighterType.Item2), 98, 145, DescriptionPaint); } @@ -202,6 +217,7 @@ public class BaseFighter : UCreator ImagePaint.ImageFilter = null; ImagePaint.BlendMode = SKBlendMode.SoftLight; c.DrawBitmap(_perk, new SKRect(x, y, x + size / 2, y + size / 2), ImagePaint); + if (_recommendedPerks.Count < 1) return; ImagePaint.BlendMode = SKBlendMode.SrcOver; ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 2.5f, 2.5f, SKColors.Black); @@ -222,6 +238,7 @@ public class BaseFighter : UCreator ImagePaint.ImageFilter = null; ImagePaint.BlendMode = SKBlendMode.SoftLight; c.DrawBitmap(_emote, new SKRect(x, y - size / 2, x + size / 2, y), ImagePaint); + if (_availableTaunts.Count < 1) return; ImagePaint.BlendMode = SKBlendMode.SrcOver; ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 1.5f, 1.5f, SKColors.Black); @@ -242,6 +259,7 @@ public class BaseFighter : UCreator ImagePaint.ImageFilter = null; ImagePaint.BlendMode = SKBlendMode.SoftLight; c.DrawBitmap(_skin, new SKRect(x, y, x + size / 4, y + size / 4), ImagePaint); + if (_skins.Count < 1) return; ImagePaint.BlendMode = SKBlendMode.SrcOver; ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 1.5f, 1.5f, SKColors.Black); @@ -262,3 +280,10 @@ public enum EFighterClass : byte Assassin = 1, Support = 0 // Default } + +public enum EFighterType : byte +{ + Hybrid = 2, + Vertical = 1, + Horizontal = 0 // Default +} diff --git a/FModel/Creator/Bases/MV/BaseMultiversusIcon.cs b/FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs similarity index 50% rename from FModel/Creator/Bases/MV/BaseMultiversusIcon.cs rename to FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs index a25c044b..85cc1117 100644 --- a/FModel/Creator/Bases/MV/BaseMultiversusIcon.cs +++ b/FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs @@ -1,5 +1,4 @@ using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.UObject; @@ -8,71 +7,50 @@ using SkiaSharp; namespace FModel.Creator.Bases.MV; -public class BaseMultiversusIcon : BaseIcon +public class BaseMultiVersusIcon : BaseIcon { - public BaseMultiversusIcon(UObject uObject, EIconStyle style) : base(uObject, style) + public BaseMultiVersusIcon(UObject uObject, EIconStyle style) : base(uObject, style) { + DefaultPreview = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); } public override void ParseForInfo() { - if (Object.TryGetValue(out var backgroundColor, "BackgroundColor")) - { - var bgColor = SKColor.Parse(backgroundColor.Hex); - Background = new[] { bgColor, bgColor }; - } - else if (Object.TryGetValue(out var rarity, "Rarity")) - { - Background = GetRarityBackground(rarity.ToString()); - Border = new[] { GetRarityBorder(rarity.ToString()) }; - } + var rarity = Object.GetOrDefault("Rarity", new FName("ERewardRarity::None")); + Background = GetRarityBackground(rarity.ToString()); + Border = new[] { GetRarityBorder(rarity.ToString()) }; - if (Object.TryGetValue(out var nameColor, "DisplayNameColor")) - { - Border = new[] { SKColor.Parse(nameColor.Hex) }; - } - - if (Object.TryGetValue(out var rewardThumbnail, "RewardThumbnail")) + if (Object.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail", "DisplayTextureRef")) Preview = Utils.GetBitmap(rewardThumbnail); + else if (Object.TryGetValue(out FPackageIndex icon, "Icon")) + Preview = Utils.GetBitmap(icon); - else if (Object.TryGetValue(out var portaitPtr, "CollectionsPortraitMaterial", "CharacterSelectPortraitMaterial") - && portaitPtr.TryLoad(out var portait)) - Preview = Utils.GetBitmap(portait); - - else if (Object.TryGetValue(out var skins, "Skins") && - skins[0].TryLoad(out var defaultSkin) && - defaultSkin.TryGetValue(out var skinRewardThumb, "RewardThumbnail")) - Preview = Utils.GetBitmap(skinRewardThumb); - - else if (Object.TryGetValue(out var eogPortrait, "EOGPortrait")) - Preview = Utils.GetBitmap(eogPortrait); - - if (Object.TryGetValue(out var displayName, "DisplayName")) + if (Object.TryGetValue(out FText displayName, "DisplayName")) DisplayName = displayName.Text; + if (Object.TryGetValue(out FText description, "Description")) + Description = Utils.RemoveHtmlTags(description.Text); - if (Object.TryGetValue(out var description, "Overview", "Bio")) - Description = description.Text; - - if (Object.TryGetValue(out var property, "Property")) - ShortDescription = property.Text; - - if (Object.TryGetValue(out var unlockLocation, "UnlockLocation")) - CosmeticSource = unlockLocation.ToString().Split("::")[1]; + if (Object.TryGetValue(out int xpValue, "XPValue")) + DisplayName += $" (+{xpValue})"; } - private static SKColor[] GetRarityBackground(string rarity) - { - string rarityName = rarity.Split("::")[1]; + // public override SKBitmap[] Draw() + // { + // // dedicated design here, use : UCreator + // throw new System.NotImplementedException(); + // } - switch (rarityName) // the colors here are the base color and brighter color that the game uses for rarities from the "Rarity to Color" blueprint function + private SKColor[] GetRarityBackground(string rarity) + { + switch (rarity.Split("::")[1]) // the colors here are the base color and brighter color that the game uses for rarities from the "Rarity to Color" blueprint function { - case "Common": + case "Common": return new[] { SKColor.Parse(new FLinearColor(0.068478f, 0.651406f, 0.016807f, 1.000000f).Hex), SKColor.Parse(new FLinearColor(0.081422f, 1.000000f, 0.000000f, 1.000000f).Hex) }; - case "Rare": + case "Rare": return new[] { SKColor.Parse(new FLinearColor(0.035911f, 0.394246f, 0.900000f, 1.000000f).Hex), @@ -100,11 +78,9 @@ public class BaseMultiversusIcon : BaseIcon } } - private static SKColor GetRarityBorder(string rarity) + private SKColor GetRarityBorder(string rarity) { - string rarityName = rarity.Split("::")[1]; - - switch (rarityName) // the colors here are the desaturated versions of the rarity colors + switch (rarity.Split("::")[1]) // the colors here are the desaturated versions of the rarity colors { case "Common": return SKColor.Parse(new FLinearColor(0.172713f, 0.651406f, 0.130281f, 1.000000f).Hex); diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 4bf2b4e1..678dee48 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -178,8 +178,20 @@ public class CreatorPackage : IDisposable case "CharacterData": creator = new BaseFighter(_object, _style); return true; + case "PerkGroup": + creator = null; + return true; + case "StatTrackingBundleData": + case "HydraSyncedDataAsset": + case "CharacterGiftData": + case "ProfileIconData": + case "RingOutVfxData": + case "BannerData": + case "EmoteData": + case "TauntData": case "SkinData": - creator = new BaseMultiversusIcon(_object, _style); + case "PerkData": + creator = new BaseMultiVersusIcon(_object, _style); return true; // Battle Breakers case "WExpGenericAccountItemDefinition": diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs index 8527d23e..ef7afd25 100644 --- a/FModel/Creator/Typefaces.cs +++ b/FModel/Creator/Typefaces.cs @@ -44,7 +44,9 @@ public class Typefaces // Multiversus private const string _MULTIVERSUS_BASE_PATH = "/Game/Panda_Main/UI/Fonts/"; private const string _NORMS_STD_CONDENSED_BLACK = "Norms/TT_Norms_Std_Condensed_Black"; + private const string _XIANGHEHEI_SC_PRO_BLACK = "XiangHeHei_SC/MXiangHeHeiSCPro-Black"; private const string _NORMS_STD_CONDENSED_BOLD = "Norms/TT_Norms_Std_Condensed_Bold"; + private const string _XIANGHEHEI_SC_PRO_HEAVY = "XiangHeHei_SC/MXiangHeHeiSCPro-Heavy"; // Spellbreak private const string _SPELLBREAK_BASE_PATH = "/Game/UI/Fonts/"; @@ -200,8 +202,16 @@ public class Typefaces } case FGame.Multiversus: { - DisplayName = OnTheFly(_MULTIVERSUS_BASE_PATH + _NORMS_STD_CONDENSED_BLACK + _EXT); - Description = OnTheFly(_MULTIVERSUS_BASE_PATH + _NORMS_STD_CONDENSED_BOLD + _EXT); + DisplayName = OnTheFly(_MULTIVERSUS_BASE_PATH + language switch + { + ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK, + _ => _NORMS_STD_CONDENSED_BLACK + } + _EXT); + Description = OnTheFly(_MULTIVERSUS_BASE_PATH + language switch + { + ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY, + _ => _NORMS_STD_CONDENSED_BOLD + } + _EXT); break; } default: diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index afb5ce54..83238348 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -66,7 +66,9 @@ public partial class MainWindow await _applicationView.CUE4Parse.Initialize(); await _applicationView.AesManager.InitAes(); await _applicationView.AesManager.UpdateProvider(true); +#if !DEBUG await _applicationView.CUE4Parse.InitInformation(); +#endif await _applicationView.CUE4Parse.InitBenMappings(); await _applicationView.InitVgmStream(); await _applicationView.InitOodle(); From 263c213dcc86b69cc0f3eed3149e28407f8485c4 Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 2 Aug 2022 18:43:48 +0200 Subject: [PATCH 010/178] rip fonts --- CUE4Parse | 2 +- FModel/App.xaml.cs | 6 +- FModel/Creator/Bases/MV/BaseFighter.cs | 46 ++- .../Creator/Bases/MV/BaseMultiVersusIcon.cs | 98 ------ FModel/Creator/Bases/MV/BasePandaIcon.cs | 330 ++++++++++++++++++ FModel/Creator/Bases/MV/BasePerkGroup.cs | 45 +++ FModel/Creator/CreatorPackage.cs | 7 +- FModel/Creator/Typefaces.cs | 26 +- FModel/Creator/Utils.cs | 8 +- FModel/Enums.cs | 4 +- FModel/Settings/UserSettings.cs | 15 +- FModel/ViewModels/GameSelectorViewModel.cs | 1 - .../Converters/StringToGameConverter.cs | 2 +- 13 files changed, 440 insertions(+), 150 deletions(-) delete mode 100644 FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs create mode 100644 FModel/Creator/Bases/MV/BasePandaIcon.cs create mode 100644 FModel/Creator/Bases/MV/BasePerkGroup.cs diff --git a/CUE4Parse b/CUE4Parse index 403599b4..061ad8f5 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 403599b4939e81cd7b4f3d3b201cf1018e02ece0 +Subproject commit 061ad8f578b7493b892d6391c4901dd05ca43a16 diff --git a/FModel/App.xaml.cs b/FModel/App.xaml.cs index e991c0ad..b09146f7 100644 --- a/FModel/App.xaml.cs +++ b/FModel/App.xaml.cs @@ -2,9 +2,9 @@ using Microsoft.Win32; using Serilog; using System; +using System.Globalization; using System.IO; using System.Runtime.InteropServices; -using System.Threading; using System.Windows; using System.Windows.Threading; using FModel.Framework; @@ -96,7 +96,7 @@ public partial class App Log.Information("Version {Version}", Constants.APP_VERSION); Log.Information("{OS}", GetOperatingSystemProductName()); Log.Information("{RuntimeVer}", RuntimeInformation.FrameworkDescription); - Log.Information("Culture {SysLang}", Thread.CurrentThread.CurrentUICulture); + Log.Information("Culture {SysLang}", CultureInfo.CurrentCulture); } private void AppExit(object sender, ExitEventArgs e) @@ -162,4 +162,4 @@ public partial class App return rk.GetValue(name, null) as string; return string.Empty; } -} \ No newline at end of file +} diff --git a/FModel/Creator/Bases/MV/BaseFighter.cs b/FModel/Creator/Bases/MV/BaseFighter.cs index fc5da665..4d8128a3 100644 --- a/FModel/Creator/Bases/MV/BaseFighter.cs +++ b/FModel/Creator/Bases/MV/BaseFighter.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Collections.Generic; +using System.ComponentModel; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Exports.Engine; using CUE4Parse.UE4.Assets.Exports.Material; @@ -8,6 +9,7 @@ using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Objects.Core.i18N; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.UObject; +using FModel.Extensions; using SkiaSharp; namespace FModel.Creator.Bases.MV; @@ -31,15 +33,17 @@ public class BaseFighter : UCreator public BaseFighter(UObject uObject, EIconStyle style) : base(uObject, style) { Width = 1024; - DisplayNamePaint.TextAlign = SKTextAlign.Left; DisplayNamePaint.TextSize = 100; + DisplayNamePaint.TextAlign = SKTextAlign.Left; + DisplayNamePaint.Typeface = Utils.Typefaces.TandemDisplayName; DescriptionPaint.TextSize = 25; - DefaultPreview = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); + DescriptionPaint.Typeface = Utils.Typefaces.TandemGenDescription; + DefaultPreview = Utils.GetBitmap("/Game/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); - _pattern = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/UI_Textures/halftone_jagged.halftone_jagged"); - _perk = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/Icons/ui_icons_perks.ui_icons_perks"); - _emote = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/Icons/ui_icons_emote.ui_icons_emote"); - _skin = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/Assets/Icons/ui_icons_skins.ui_icons_skins"); + _pattern = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/UI_Textures/halftone_jagged.halftone_jagged"); + _perk = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_perks.ui_icons_perks"); + _emote = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_emote.ui_icons_emote"); + _skin = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_skins.ui_icons_skins"); _fighterType.Item2 = new List(); _recommendedPerks = new List(); _availableTaunts = new List(); @@ -66,7 +70,7 @@ public class BaseFighter : UCreator DisplayName = displayName.Text; GetFighterClassInfo(Object.GetOrDefault("Class", EFighterClass.Support)); - _fighterType.Item2.Add(GetFighterType(Object.GetOrDefault("Type", EFighterType.Horizontal))); + _fighterType.Item2.Add(Utils.GetLocalizedResource(Object.GetOrDefault("Type", EFighterType.Horizontal))); if (Object.TryGetValue(out FText property, "Property")) _fighterType.Item2.Add(property.Text); @@ -126,7 +130,7 @@ public class BaseFighter : UCreator private void GetFighterClassInfo(EFighterClass clas) { - if (!Utils.TryLoadObject("MultiVersus/Content/Panda_Main/UI/In-Game/Data/UICharacterClassInfo_Datatable.UICharacterClassInfo_Datatable", out UDataTable dataTable)) + if (!Utils.TryLoadObject("/Game/Panda_Main/UI/In-Game/Data/UICharacterClassInfo_Datatable.UICharacterClassInfo_Datatable", out UDataTable dataTable)) return; var row = dataTable.RowMap.ElementAt((int) clas).Value; @@ -138,17 +142,6 @@ public class BaseFighter : UCreator _fighterType.Item2.Add(displayName.Text); } - private string GetFighterType(EFighterType typ) - { - return typ switch - { - EFighterType.Horizontal => Utils.GetLocalizedResource("", "97A60DD54AA23D4B93D5B891F729BF5C", "Horizontal"), - EFighterType.Vertical => Utils.GetLocalizedResource("", "2C55443D47164019BE73A5ABDC670F36", "Vertical"), - EFighterType.Hybrid => Utils.GetLocalizedResource("", "B980C82D40FF37FD359C74A339CE1B3A", "Hybrid"), - _ => typ.ToString() - }; - } - private new void DrawBackground(SKCanvas c) { c.DrawRect(new SKRect(0, 0, Width, Height), @@ -167,16 +160,16 @@ public class BaseFighter : UCreator }); } - c.DrawBitmap(_pattern, new SKRect(0, 256, Width, 512), new SKPaint + c.DrawBitmap(_pattern, new SKRect(0, Height / 2, Width, Height), new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High, BlendMode = SKBlendMode.SoftLight }); var path = new SKPath { FillType = SKPathFillType.EvenOdd }; - path.MoveTo(0, 512); - path.LineTo(0, 492); - path.LineTo(Width, 452); - path.LineTo(Width, 512); + path.MoveTo(0, Height); + path.LineTo(0, Height - 20); + path.LineTo(Width, Height - 60); + path.LineTo(Width, Height); path.Close(); c.DrawPath(path, new SKPaint { @@ -283,7 +276,12 @@ public enum EFighterClass : byte public enum EFighterType : byte { + [Description("B980C82D40FF37FD359C74A339CE1B3A")] Hybrid = 2, + + [Description("2C55443D47164019BE73A5ABDC670F36")] Vertical = 1, + + [Description("97A60DD54AA23D4B93D5B891F729BF5C")] Horizontal = 0 // Default } diff --git a/FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs b/FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs deleted file mode 100644 index 85cc1117..00000000 --- a/FModel/Creator/Bases/MV/BaseMultiVersusIcon.cs +++ /dev/null @@ -1,98 +0,0 @@ -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Objects.Core.i18N; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.UObject; -using FModel.Creator.Bases.FN; -using SkiaSharp; - -namespace FModel.Creator.Bases.MV; - -public class BaseMultiVersusIcon : BaseIcon -{ - public BaseMultiVersusIcon(UObject uObject, EIconStyle style) : base(uObject, style) - { - DefaultPreview = Utils.GetBitmap("MultiVersus/Content/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); - } - - public override void ParseForInfo() - { - var rarity = Object.GetOrDefault("Rarity", new FName("ERewardRarity::None")); - Background = GetRarityBackground(rarity.ToString()); - Border = new[] { GetRarityBorder(rarity.ToString()) }; - - if (Object.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail", "DisplayTextureRef")) - Preview = Utils.GetBitmap(rewardThumbnail); - else if (Object.TryGetValue(out FPackageIndex icon, "Icon")) - Preview = Utils.GetBitmap(icon); - - if (Object.TryGetValue(out FText displayName, "DisplayName")) - DisplayName = displayName.Text; - if (Object.TryGetValue(out FText description, "Description")) - Description = Utils.RemoveHtmlTags(description.Text); - - if (Object.TryGetValue(out int xpValue, "XPValue")) - DisplayName += $" (+{xpValue})"; - } - - // public override SKBitmap[] Draw() - // { - // // dedicated design here, use : UCreator - // throw new System.NotImplementedException(); - // } - - private SKColor[] GetRarityBackground(string rarity) - { - switch (rarity.Split("::")[1]) // the colors here are the base color and brighter color that the game uses for rarities from the "Rarity to Color" blueprint function - { - case "Common": - return new[] - { - SKColor.Parse(new FLinearColor(0.068478f, 0.651406f, 0.016807f, 1.000000f).Hex), - SKColor.Parse(new FLinearColor(0.081422f, 1.000000f, 0.000000f, 1.000000f).Hex) - }; - case "Rare": - return new[] - { - SKColor.Parse(new FLinearColor(0.035911f, 0.394246f, 0.900000f, 1.000000f).Hex), - SKColor.Parse(new FLinearColor(0.033333f, 0.434207f, 1.000000f, 1.000000f).Hex) - }; - case "Epic": - return new[] - { - SKColor.Parse(new FLinearColor(0.530391f, 0.060502f, 0.900000f, 1.000000f).Hex), - SKColor.Parse(new FLinearColor(0.579907f, 0.045833f, 1.000000f, 1.000000f).Hex) - }; - case "Legendary": - return new[] - { - SKColor.Parse(new FLinearColor(1.000000f, 0.223228f, 0.002428f, 1.000000f).Hex), - SKColor.Parse(new FLinearColor(1.000000f, 0.479320f, 0.030713f, 1.000000f).Hex) - }; - case "None": - default: - return new[] - { - SKColor.Parse(new FLinearColor(0.194618f, 0.651406f, 0.630757f, 1.000000f).Hex), - SKColor.Parse(new FLinearColor(0.273627f, 0.955208f, 0.914839f, 1.000000f).Hex) - }; - } - } - - private SKColor GetRarityBorder(string rarity) - { - switch (rarity.Split("::")[1]) // the colors here are the desaturated versions of the rarity colors - { - case "Common": - return SKColor.Parse(new FLinearColor(0.172713f, 0.651406f, 0.130281f, 1.000000f).Hex); - case "Rare": - return SKColor.Parse(new FLinearColor(0.198220f, 0.527026f, 0.991102f, 1.000000f).Hex); - case "Epic": - return SKColor.Parse(new FLinearColor(0.642017f, 0.198220f, 0.991102f, 1.000000f).Hex); - case "Legendary": - return SKColor.Parse(new FLinearColor(1.000000f, 0.328434f, 0.200000f, 1.000000f).Hex); - case "None": - default: - return SKColor.Parse(new FLinearColor(0.308843f, 0.571125f, 0.557810f, 1.000000f).Hex); - } - } -} diff --git a/FModel/Creator/Bases/MV/BasePandaIcon.cs b/FModel/Creator/Bases/MV/BasePandaIcon.cs new file mode 100644 index 00000000..14349cbe --- /dev/null +++ b/FModel/Creator/Bases/MV/BasePandaIcon.cs @@ -0,0 +1,330 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Engine; +using CUE4Parse.UE4.Objects.Core.i18N; +using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.UObject; +using SkiaSharp; + +namespace FModel.Creator.Bases.MV; + +public class BasePandaIcon : UCreator +{ + private float _y_offset; + private ERewardRarity _rarity; + private string _type; + + private readonly List<(SKBitmap, string)> _pictos; + + public BasePandaIcon(UObject uObject, EIconStyle style) : base(uObject, style) + { + Width = 1024; + Margin = 30; + DisplayNamePaint.TextSize = 50; + DisplayNamePaint.TextAlign = SKTextAlign.Left; + DisplayNamePaint.Color = SKColor.Parse("#191C33"); + DescriptionPaint.TextSize = 25; + DefaultPreview = Utils.GetBitmap("/Game/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random"); + + _y_offset = Height / 2 + DescriptionPaint.TextSize; + _pictos = new List<(SKBitmap, string)>(); + } + + public override void ParseForInfo() + { + _type = Object.ExportType; + var t = _type switch + { + // "CharacterData" => , + "StatTrackingBundleData" => EItemType.Badge, + "AnnouncerPackData" => EItemType.Announcer, + "CharacterGiftData" => EItemType.ExperiencePoints, + "ProfileIconData" => EItemType.ProfileIcon, + "RingOutVfxData" => EItemType.Ringout, + "BannerData" => EItemType.Banner, + "EmoteData" => EItemType.Sticker, + "TauntData" => EItemType.Emote, + "SkinData" => EItemType.Variant, + "PerkData" => EItemType.Perk, + _ => EItemType.Unknown + }; + + _rarity = Object.GetOrDefault("Rarity", ERewardRarity.None); + Background = GetRarityBackground(_rarity); + + if (Object.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail", "DisplayTextureRef", "Texture")) + Preview = Utils.GetBitmap(rewardThumbnail); + else if (Object.TryGetValue(out FPackageIndex icon, "Icon")) + Preview = Utils.GetBitmap(icon); + + if (Object.TryGetValue(out FText displayName, "DisplayName")) + DisplayName = displayName.Text; + if (Object.TryGetValue(out FText description, "Description")) + Description = Utils.RemoveHtmlTags(description.Text); + + _pictos.Add(( + Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_unlocked.ui_icons_unlocked"), + Utils.GetLocalizedResource(Object.GetOrDefault("UnlockLocation", EUnlockLocation.None)))); + if (Object.TryGetValue(out string slug, "Slug")) + { + t = _type switch + { + "HydraSyncedDataAsset" when slug == "gold" => EItemType.Gold, + "HydraSyncedDataAsset" when slug == "match_toasts" => EItemType.Toast, + _ => t + }; + _pictos.Add((Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_link.ui_icons_link"), slug)); + } + + if (Object.TryGetValue(out int xpValue, "XPValue")) + DisplayName += $" (+{xpValue})"; + + if (Utils.TryLoadObject("/Game/Panda_Main/UI/Prototype/Foundation/Types/DT_EconomyGlossary.DT_EconomyGlossary", out UDataTable dataTable)) + { + if (t != EItemType.Unknown && + dataTable.RowMap.ElementAt((int) t).Value.TryGetValue(out FText name, "Name_14_7F75AD6047CBDEA7B252B1BD76EF84B9")) + _type = name.Text; + } + } + + public override SKBitmap[] Draw() + { + var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul); + using var c = new SKCanvas(ret); + + DrawBackground(c); + DrawPreview(c); + DrawDisplayName(c); + DrawDescription(c); + DrawPictos(c); + + return new[] { ret }; + } + + private SKColor[] GetRarityBackground(ERewardRarity rarity) + { + return rarity switch // the colors here are the base color and brighter color that the game uses for rarities from the "Rarity to Color" blueprint function + { + ERewardRarity.Common => new[] + { + SKColor.Parse(new FLinearColor(0.068478f, 0.651406f, 0.016807f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.081422f, 1.000000f, 0.000000f, 1.000000f).Hex) + }, + ERewardRarity.Rare => new[] + { + SKColor.Parse(new FLinearColor(0.035911f, 0.394246f, 0.900000f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.033333f, 0.434207f, 1.000000f, 1.000000f).Hex) + }, + ERewardRarity.Epic => new[] + { + SKColor.Parse(new FLinearColor(0.530391f, 0.060502f, 0.900000f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.579907f, 0.045833f, 1.000000f, 1.000000f).Hex) + }, + ERewardRarity.Legendary => new[] + { + SKColor.Parse(new FLinearColor(1.000000f, 0.223228f, 0.002428f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(1.000000f, 0.479320f, 0.030713f, 1.000000f).Hex) + }, + _ => new[] + { + SKColor.Parse(new FLinearColor(0.194618f, 0.651406f, 0.630757f, 1.000000f).Hex), + SKColor.Parse(new FLinearColor(0.273627f, 0.955208f, 0.914839f, 1.000000f).Hex) + } + }; + } + + private new void DrawBackground(SKCanvas c) + { + c.DrawRect(new SKRect(0, 0, Width, Height), + new SKPaint + { + IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = SKColor.Parse("#F3FCF0") + }); + + var has_tr = _rarity != ERewardRarity.None; + var tr = Utils.GetLocalizedResource(_rarity); + var tr_paint = new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.High, + TextAlign = SKTextAlign.Right, + TextSize = 35, + Color = SKColors.White, + Typeface = Utils.Typefaces.DisplayName + }; + + var path = new SKPath { FillType = SKPathFillType.EvenOdd }; + path.MoveTo(0, Height); + path.LineTo(14, Height); + path.LineTo(20, 20); + if (has_tr) + { + const int margin = 15; + var width = tr_paint.MeasureText(tr); + path.LineTo(Width - width - margin * 2, 15); + path.LineTo(Width - width - margin * 2.5f, 60); + path.LineTo(Width, 55); + } + else + { + path.LineTo(Width, 14); + } + path.LineTo(Width, 0); + path.LineTo(0, 0); + path.LineTo(0, Height); + path.Close(); + c.DrawPath(path, new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.High, + Shader = SKShader.CreateLinearGradient( + new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4), Background, SKShaderTileMode.Clamp) + }); + + if (has_tr) + { + var x = Width - 20f; + foreach (var a in tr.Select(character => character.ToString()).Reverse()) + { + c.DrawText(a, x, 40, tr_paint); + x -= tr_paint.MeasureText(a) - 2; + } + } + } + + private new void DrawPreview(SKCanvas c) + { + const int size = 384; + var y = Height - size - Margin * 2; + c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, y, size + Margin, y + size), ImagePaint); + } + + private new void DrawDisplayName(SKCanvas c) + { + if (string.IsNullOrWhiteSpace(DisplayName)) + return; + + var x = 450f; + var y = Height / 2 - DisplayNamePaint.TextSize / 4; + while (DisplayNamePaint.MeasureText(DisplayName) > Width - x) + { + DisplayNamePaint.TextSize -= 1; + } + + foreach (var a in DisplayName.Select(character => character.ToString())) + { + c.DrawText(a, x, y, DisplayNamePaint); + x += DisplayNamePaint.MeasureText(a) - 4; + } + } + + private new void DrawDescription(SKCanvas c) + { + const int x = 450; + DescriptionPaint.Color = Background[0]; + c.DrawText(_type.ToUpper(), x, 170, DescriptionPaint); + + if (string.IsNullOrWhiteSpace(Description)) return; + + DescriptionPaint.Color = SKColor.Parse("#191C33"); + Utils.DrawMultilineText(c, Description, Width - x, Margin, SKTextAlign.Left, + new SKRect(x, _y_offset, Width - Margin, Height - Margin), DescriptionPaint, out _y_offset); + } + + private void DrawPictos(SKCanvas c) + { + if (_pictos.Count < 1) return; + + const float x = 450f; + const int size = 24; + var color = SKColor.Parse("#495B6E"); + var paint = new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.High, + TextSize = 27, + Color = color, + Typeface = Utils.Typefaces.Default + }; + + ImagePaint.ColorFilter = SKColorFilter.CreateBlendMode(color, SKBlendMode.SrcIn); + + foreach (var picto in _pictos) + { + c.DrawBitmap(picto.Item1, new SKRect(x, _y_offset + 10, x + size, _y_offset + 10 + size), ImagePaint); + c.DrawText(picto.Item2, x + size + 10, _y_offset + size + 6, paint); + _y_offset += size + 5; + } + } +} + +public enum ERewardRarity : byte +{ + [Description("0D4B15CE4FB6F2BC5E5F5FAA9E8B376C")] + None = 0, // Default + + [Description("0FCDEF47485E2C3D0D477988C481D8E3")] + Common = 1, + + [Description("18241CA7441AE16AAFB6EFAB499FF981")] + Rare = 2, + + [Description("D999D9CB4754D1078BF9A1B34A231005")] + Epic = 3, + + [Description("705AE967407D6EF8870E988A08C6900E")] + Legendary = 4 +} + +public enum EUnlockLocation : byte +{ + [Description("0D4B15CE4FB6F2BC5E5F5FAA9E8B376C")] + None = 0, // Default + + [Description("0AFBCE5F41D930D6E9B5138C8EBCFE87")] + Shop = 1, + + [Description("062F178B4EE74502C9AD9D878F3D7CEA")] + AccountLevel = 2, + + [Description("1AE7A5DF477B2B5F4B3CCC8DCD732884")] + CharacterMastery = 3, + + [Description("0B37731C49DC9AE1EAC566950C1A329D")] + Battlepass = 4, + + [Description("16F160084187479E5D471786190AE5B7")] + CharacterAffinity = 5, + + [Description("E5C1E35C406C585E83B5D18A817FA0B4")] + GuildBoss = 6, + + [Description("4A89F5DD432113750EF52D8B58977DCE")] + Tutorial = 7 +} + +public enum EItemType +{ + Unknown = -1, + Announcer, + Badge, + Banner, + BattlePassPoints, + Emote, + ExperiencePoints, + Gleamium, + Gold, + MasteryLevel, + Mission, + Perk, + PlayerLevel, + ProfileIcon, + Rested, + Ringout, + SignaturePerk, + Sticker, + Toast, + Variant +} diff --git a/FModel/Creator/Bases/MV/BasePerkGroup.cs b/FModel/Creator/Bases/MV/BasePerkGroup.cs new file mode 100644 index 00000000..e580c4cb --- /dev/null +++ b/FModel/Creator/Bases/MV/BasePerkGroup.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Objects; +using CUE4Parse.UE4.Objects.UObject; +using SkiaSharp; + +namespace FModel.Creator.Bases.MV; + +public class BasePerkGroup : UCreator +{ + private readonly List _perks; + + public BasePerkGroup(UObject uObject, EIconStyle style) : base(uObject, style) + { + _perks = new List(); + } + + public override void ParseForInfo() + { + if (Object.TryGetValue(out UScriptSet perks, "Perks")) // PORCO DIO WB USE ARRAYS!!!!!! + { + foreach (var perk in perks.Properties) + { + if (perk.GenericValue is not FPackageIndex packageIndex || + !Utils.TryGetPackageIndexExport(packageIndex, out UObject export)) + continue; + + var icon = new BasePandaIcon(export, Style); + icon.ParseForInfo(); + _perks.Add(icon); + } + } + } + + public override SKBitmap[] Draw() + { + var ret = new SKBitmap[_perks.Count]; + for (var i = 0; i < ret.Length; i++) + { + ret[i] = _perks[i].Draw()[0]; + } + + return ret; + } +} diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 678dee48..84e6fd4f 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -174,15 +174,16 @@ public class CreatorPackage : IDisposable case "PlaylistUserOptionCollisionProfileEnum": creator = new BaseUserControl(_object, _style); return true; - // Multiversus + // PandaGame case "CharacterData": creator = new BaseFighter(_object, _style); return true; case "PerkGroup": - creator = null; + creator = new BasePerkGroup(_object, _style); return true; case "StatTrackingBundleData": case "HydraSyncedDataAsset": + case "AnnouncerPackData": case "CharacterGiftData": case "ProfileIconData": case "RingOutVfxData": @@ -191,7 +192,7 @@ public class CreatorPackage : IDisposable case "TauntData": case "SkinData": case "PerkData": - creator = new BaseMultiVersusIcon(_object, _style); + creator = new BasePandaIcon(_object, _style); return true; // Battle Breakers case "WExpGenericAccountItemDefinition": diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs index ef7afd25..bf5268c0 100644 --- a/FModel/Creator/Typefaces.cs +++ b/FModel/Creator/Typefaces.cs @@ -41,11 +41,12 @@ public class Typefaces private const string _BURBANK_SMALL_BLACK = "burbanksmall-black"; private const string _BURBANK_SMALL_BOLD = "burbanksmall-bold"; - // Multiversus - private const string _MULTIVERSUS_BASE_PATH = "/Game/Panda_Main/UI/Fonts/"; + // PandaGame + private const string _PANDAGAME_BASE_PATH = "/Game/Panda_Main/UI/Fonts/"; private const string _NORMS_STD_CONDENSED_BLACK = "Norms/TT_Norms_Std_Condensed_Black"; - private const string _XIANGHEHEI_SC_PRO_BLACK = "XiangHeHei_SC/MXiangHeHeiSCPro-Black"; + private const string _NORMS_PRO_BLACK_ITALIC = "Norms/TT_Norms_Pro_Black_Italic"; private const string _NORMS_STD_CONDENSED_BOLD = "Norms/TT_Norms_Std_Condensed_Bold"; + private const string _XIANGHEHEI_SC_PRO_BLACK = "XiangHeHei_SC/MXiangHeHeiSCPro-Black"; private const string _XIANGHEHEI_SC_PRO_HEAVY = "XiangHeHei_SC/MXiangHeHeiSCPro-Heavy"; // Spellbreak @@ -200,14 +201,27 @@ public class Typefaces Description = OnTheFly(_SPELLBREAK_BASE_PATH + _MONTSERRAT_SEMIBOLD + _EXT); break; } - case FGame.Multiversus: + case FGame.PandaGame: { - DisplayName = OnTheFly(_MULTIVERSUS_BASE_PATH + language switch + DisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch + { + ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY, + _ => _NORMS_PRO_BLACK_ITALIC + } + _EXT); + + Description = OnTheFly(_PANDAGAME_BASE_PATH + language switch + { + ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK, + _ => _NORMS_STD_CONDENSED_BOLD + } + _EXT); + + TandemDisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch { ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK, _ => _NORMS_STD_CONDENSED_BLACK } + _EXT); - Description = OnTheFly(_MULTIVERSUS_BASE_PATH + language switch + + TandemGenDescription = OnTheFly(_PANDAGAME_BASE_PATH + language switch { ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY, _ => _NORMS_STD_CONDENSED_BOLD diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index 216d7a02..8035f74d 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -9,9 +10,9 @@ using CUE4Parse.UE4.Assets.Exports.Material; using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.UObject; using CUE4Parse.UE4.Versions; -using CUE4Parse.Utils; using CUE4Parse_Conversion.Textures; using FModel.Framework; +using FModel.Extensions; using FModel.Services; using FModel.Settings; using FModel.ViewModels; @@ -183,6 +184,11 @@ public static class Utils { return _applicationView.CUE4Parse.Provider.GetLocalizedString(@namespace, key, defaultValue); } + public static string GetLocalizedResource(T @enum) where T : Enum + { + var resource = _applicationView.CUE4Parse.Provider.GetLocalizedString("", @enum.GetDescription(), @enum.ToString()); + return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(resource.ToLower()); + } public static string GetFullPath(string partialPath) { diff --git a/FModel/Enums.cs b/FModel/Enums.cs index d40cf8a8..1b678fc1 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -92,8 +92,8 @@ public enum FGame Gameface, [Description("Sea of Thieves")] Athena, - [Description("Multiversus")] - Multiversus + [Description("That Banned Panda")] + PandaGame } public enum ELoadingMode diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index b84b34e5..82d238b1 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -287,7 +287,7 @@ namespace FModel.Settings {FGame.PortalWars, Constants._NO_PRESET_TRIGGER}, {FGame.Gameface, Constants._NO_PRESET_TRIGGER}, {FGame.Athena, Constants._NO_PRESET_TRIGGER}, - {FGame.Multiversus, Constants._NO_PRESET_TRIGGER} + {FGame.PandaGame, Constants._NO_PRESET_TRIGGER} }; public IDictionary Presets { @@ -316,7 +316,7 @@ namespace FModel.Settings {FGame.PortalWars, EGame.GAME_UE4_LATEST}, {FGame.Gameface, EGame.GAME_GTATheTrilogyDefinitiveEdition}, {FGame.Athena, EGame.GAME_SeaOfThieves}, - {FGame.Multiversus, EGame.GAME_UE4_26} + {FGame.PandaGame, EGame.GAME_UE4_26} }; public IDictionary OverridedGame { @@ -345,7 +345,7 @@ namespace FModel.Settings {FGame.PortalWars, null}, {FGame.Gameface, null}, {FGame.Athena, null}, - {FGame.Multiversus, null} + {FGame.PandaGame, null} }; public IDictionary> OverridedCustomVersions { @@ -374,7 +374,7 @@ namespace FModel.Settings {FGame.PortalWars, null}, {FGame.Gameface, null}, {FGame.Athena, null}, - {FGame.Multiversus, null} + {FGame.PandaGame, null} }; public IDictionary> OverridedOptions { @@ -439,12 +439,7 @@ namespace FModel.Settings new("Strings", "g3/Content/Localization/") } }, - { - FGame.Multiversus, new List() - { - new("Characters", "MultiVersus/Content/Panda_Main/Characters/") - } - }, + {FGame.PandaGame, new List()}, {FGame.StateOfDecay2, new List()}, {FGame.Prospect, new List()}, {FGame.Indiana, new List()}, diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index 966c14d1..62272ffb 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -109,7 +109,6 @@ public class GameSelectorViewModel : ViewModel yield return GetUnrealEngineGame("Snoek", "\\StateOfDecay2\\Content\\Paks"); yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508", "\\Core\\Platform\\Content\\Paks"); yield return GetUnrealEngineGame("Nebula", "\\BendGame\\Content"); - yield return GetUnrealEngineGame("711c5e95dc094ca58e5f16bd48e751d6", "\\MultiVersus\\Content"); yield return GetRiotGame("VALORANT", "ShooterGame\\Content\\Paks"); yield return new DetectedGame { GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER }; yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks"); diff --git a/FModel/Views/Resources/Converters/StringToGameConverter.cs b/FModel/Views/Resources/Converters/StringToGameConverter.cs index bc856ef6..1fd44fe0 100644 --- a/FModel/Views/Resources/Converters/StringToGameConverter.cs +++ b/FModel/Views/Resources/Converters/StringToGameConverter.cs @@ -25,7 +25,7 @@ public class StringToGameConverter : IValueConverter "MinecraftDungeons" => "Minecraft Dungeons", "shoebill" => "Star Wars: Jedi Fallen Order", "a99769d95d8f400baad1f67ab5dfe508" => "Core", - "711c5e95dc094ca58e5f16bd48e751d6" => "MultiVersus", + "711c5e95dc094ca58e5f16bd48e751d6" => "That Banned Panda", 381210 => "Dead By Daylight", 578080 => "PLAYERUNKNOWN'S BATTLEGROUNDS", 677620 => "Splitgate", From 4e6e5448e5fd59db22fd6c8df4beb55cda38edf2 Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 2 Aug 2022 19:57:53 +0200 Subject: [PATCH 011/178] prout --- FModel/ViewModels/CUE4ParseViewModel.cs | 6 ++++- FModel/ViewModels/GameSelectorViewModel.cs | 30 ++++++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index ed4ea96c..b88304bb 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -275,7 +275,11 @@ public class CUE4ParseViewModel : ViewModel file.FileCount = vfs.FileCount; } - Game = Provider.GameName.ToEnum(Game); + if (Provider.GameName.Length == 11 && + Provider.GameName.StartsWith("mu") && + Provider.GameName.EndsWith("sus")) + Game = FGame.PandaGame; + else Game = Provider.GameName.ToEnum(Game); }); } diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index 62272ffb..61381c67 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -97,18 +97,19 @@ public class GameSelectorViewModel : ViewModel private IEnumerable EnumerateDetectedGames() { - yield return GetUnrealEngineGame("Fortnite", "\\FortniteGame\\Content\\Paks"); + yield return GetUnrealEngineGame("Fortnite"); yield return new DetectedGame { GameName = "Fortnite [LIVE]", GameDirectory = Constants._FN_LIVE_TRIGGER }; - yield return GetUnrealEngineGame("Pewee", "\\RogueCompany\\Content\\Paks"); - yield return GetUnrealEngineGame("Rosemallow", "\\Indiana\\Content\\Paks"); - yield return GetUnrealEngineGame("Catnip", "\\OakGame\\Content\\Paks"); - yield return GetUnrealEngineGame("AzaleaAlpha", "\\Prospect\\Content\\Paks"); - yield return GetUnrealEngineGame("WorldExplorersLive", "\\WorldExplorers\\Content"); - yield return GetUnrealEngineGame("Newt", "\\g3\\Content\\Paks"); - yield return GetUnrealEngineGame("shoebill", "\\SwGame\\Content\\Paks"); - yield return GetUnrealEngineGame("Snoek", "\\StateOfDecay2\\Content\\Paks"); - yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508", "\\Core\\Platform\\Content\\Paks"); - yield return GetUnrealEngineGame("Nebula", "\\BendGame\\Content"); + yield return GetUnrealEngineGame("Pewee"); + yield return GetUnrealEngineGame("Rosemallow"); + yield return GetUnrealEngineGame("Catnip"); + yield return GetUnrealEngineGame("AzaleaAlpha"); + yield return GetUnrealEngineGame("WorldExplorersLive"); + yield return GetUnrealEngineGame("Newt"); + yield return GetUnrealEngineGame("shoebill"); + yield return GetUnrealEngineGame("Snoek"); + yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508"); + yield return GetUnrealEngineGame("Nebula"); + yield return GetUnrealEngineGame("711c5e95dc094ca58e5f16bd48e751d6"); yield return GetRiotGame("VALORANT", "ShooterGame\\Content\\Paks"); yield return new DetectedGame { GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER }; yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks"); @@ -122,7 +123,7 @@ public class GameSelectorViewModel : ViewModel } private LauncherInstalled _launcherInstalled; - private DetectedGame GetUnrealEngineGame(string gameName, string pakDirectory) + private DetectedGame GetUnrealEngineGame(string gameName) { _launcherInstalled ??= GetDriveLauncherInstalls("ProgramData\\Epic\\UnrealEngineLauncher\\LauncherInstalled.dat"); if (_launcherInstalled?.InstallationList != null) @@ -130,7 +131,10 @@ public class GameSelectorViewModel : ViewModel foreach (var installationList in _launcherInstalled.InstallationList) { if (installationList.AppName.Equals(gameName, StringComparison.OrdinalIgnoreCase)) - return new DetectedGame { GameName = installationList.AppName, GameDirectory = $"{installationList.InstallLocation}{pakDirectory}" }; + { + var pak = Directory.GetDirectories(installationList.InstallLocation, "Paks*", SearchOption.AllDirectories); + return new DetectedGame { GameName = installationList.AppName, GameDirectory = pak[0] }; + } } } From bc276e171b416ea322444fff8ca06b94d98017be Mon Sep 17 00:00:00 2001 From: 4sval Date: Tue, 2 Aug 2022 20:10:26 +0200 Subject: [PATCH 012/178] eheh --- FModel/Helper.cs | 7 ++++++- FModel/ViewModels/CUE4ParseViewModel.cs | 9 +++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/FModel/Helper.cs b/FModel/Helper.cs index 8e1586c8..cee8cb20 100644 --- a/FModel/Helper.cs +++ b/FModel/Helper.cs @@ -16,6 +16,11 @@ public static class Helper internal readonly ulong UlongValue; } + public static bool IAmThePanda(string key) + => key.Length == 11 && + key.StartsWith("mu", StringComparison.OrdinalIgnoreCase) && + key.EndsWith("sus", StringComparison.OrdinalIgnoreCase); + public static void OpenWindow(string windowName, Action action) where T : Window { if (!IsWindowOpen(windowName)) @@ -84,4 +89,4 @@ public static class Helper var d = (Math.Abs(d1) + Math.Abs(d2) + 10) * 1.0e-15; return -d < n && d > n; } -} \ No newline at end of file +} diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index b88304bb..c1eeb273 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -103,7 +103,8 @@ public class CUE4ParseViewModel : ViewModel } default: { - Game = gameDirectory.SubstringBeforeLast("\\Content").SubstringAfterLast("\\").ToEnum(FGame.Unknown); + var parent = gameDirectory.SubstringBeforeLast("\\Content").SubstringAfterLast("\\"); + Game = Helper.IAmThePanda(parent) ? FGame.PandaGame : parent.ToEnum(FGame.Unknown); var versions = new VersionContainer(UserSettings.Default.OverridedGame[Game], UserSettings.Default.OverridedPlatform, customVersions: UserSettings.Default.OverridedCustomVersions[Game], optionOverrides: UserSettings.Default.OverridedOptions[Game]); @@ -275,11 +276,7 @@ public class CUE4ParseViewModel : ViewModel file.FileCount = vfs.FileCount; } - if (Provider.GameName.Length == 11 && - Provider.GameName.StartsWith("mu") && - Provider.GameName.EndsWith("sus")) - Game = FGame.PandaGame; - else Game = Provider.GameName.ToEnum(Game); + Game = Helper.IAmThePanda(Provider.GameName) ? FGame.PandaGame : Provider.GameName.ToEnum(Game); }); } From ef8e08e4c76ab37c53784d7eaed100d4dddd2f18 Mon Sep 17 00:00:00 2001 From: 4sval Date: Wed, 3 Aug 2022 00:05:35 +0200 Subject: [PATCH 013/178] few fixes --- FModel/Creator/Bases/MV/BasePandaIcon.cs | 25 ++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/FModel/Creator/Bases/MV/BasePandaIcon.cs b/FModel/Creator/Bases/MV/BasePandaIcon.cs index 14349cbe..a52a9bde 100644 --- a/FModel/Creator/Bases/MV/BasePandaIcon.cs +++ b/FModel/Creator/Bases/MV/BasePandaIcon.cs @@ -34,10 +34,12 @@ public class BasePandaIcon : UCreator public override void ParseForInfo() { + var category = Object.GetOrDefault("Category", EPerkCategory.Offense); + _rarity = Object.GetOrDefault("Rarity", ERewardRarity.None); + _type = Object.ExportType; - var t = _type switch + var t = _type switch // ERewardType like { - // "CharacterData" => , "StatTrackingBundleData" => EItemType.Badge, "AnnouncerPackData" => EItemType.Announcer, "CharacterGiftData" => EItemType.ExperiencePoints, @@ -47,11 +49,12 @@ public class BasePandaIcon : UCreator "EmoteData" => EItemType.Sticker, "TauntData" => EItemType.Emote, "SkinData" => EItemType.Variant, + "PerkData" when category == EPerkCategory.CharacterSpecific => EItemType.SignaturePerk, "PerkData" => EItemType.Perk, _ => EItemType.Unknown }; - _rarity = Object.GetOrDefault("Rarity", ERewardRarity.None); + if (t == EItemType.SignaturePerk) _rarity = ERewardRarity.Legendary; Background = GetRarityBackground(_rarity); if (Object.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail", "DisplayTextureRef", "Texture")) @@ -64,14 +67,16 @@ public class BasePandaIcon : UCreator if (Object.TryGetValue(out FText description, "Description")) Description = Utils.RemoveHtmlTags(description.Text); - _pictos.Add(( - Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_unlocked.ui_icons_unlocked"), - Utils.GetLocalizedResource(Object.GetOrDefault("UnlockLocation", EUnlockLocation.None)))); + var unlockLocation = Object.GetOrDefault("UnlockLocation", EUnlockLocation.None); + if (t == EItemType.Unknown && unlockLocation == EUnlockLocation.CharacterMastery) t = EItemType.MasteryLevel; + + _pictos.Add((Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_unlocked.ui_icons_unlocked"), Utils.GetLocalizedResource(unlockLocation))); if (Object.TryGetValue(out string slug, "Slug")) { t = _type switch { "HydraSyncedDataAsset" when slug == "gold" => EItemType.Gold, + "HydraSyncedDataAsset" when slug == "gleamium" => EItemType.Gleamium, "HydraSyncedDataAsset" when slug == "match_toasts" => EItemType.Toast, _ => t }; @@ -305,6 +310,14 @@ public enum EUnlockLocation : byte Tutorial = 7 } +public enum EPerkCategory : byte +{ + Offense = 0, // Default + Defense = 1, + Utility = 2, + CharacterSpecific = 3 +} + public enum EItemType { Unknown = -1, From 746292892d9f89acb6e3d0164ec467a541bc6019 Mon Sep 17 00:00:00 2001 From: 4sval Date: Wed, 3 Aug 2022 22:55:56 +0200 Subject: [PATCH 014/178] uh --- FModel/Enums.cs | 2 +- FModel/ViewModels/GameSelectorViewModel.cs | 2 +- .../Converters/StringToGameConverter.cs | 42 ++++++++++--------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/FModel/Enums.cs b/FModel/Enums.cs index 1b678fc1..f5b29a98 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -92,7 +92,7 @@ public enum FGame Gameface, [Description("Sea of Thieves")] Athena, - [Description("That Banned Panda")] + [Description("Your Beloved ™ Panda")] PandaGame } diff --git a/FModel/ViewModels/GameSelectorViewModel.cs b/FModel/ViewModels/GameSelectorViewModel.cs index 61381c67..569d0c46 100644 --- a/FModel/ViewModels/GameSelectorViewModel.cs +++ b/FModel/ViewModels/GameSelectorViewModel.cs @@ -133,7 +133,7 @@ public class GameSelectorViewModel : ViewModel if (installationList.AppName.Equals(gameName, StringComparison.OrdinalIgnoreCase)) { var pak = Directory.GetDirectories(installationList.InstallLocation, "Paks*", SearchOption.AllDirectories); - return new DetectedGame { GameName = installationList.AppName, GameDirectory = pak[0] }; + if (pak.Length > 0) return new DetectedGame { GameName = installationList.AppName, GameDirectory = pak[0] }; } } } diff --git a/FModel/Views/Resources/Converters/StringToGameConverter.cs b/FModel/Views/Resources/Converters/StringToGameConverter.cs index 1fd44fe0..1b1b6456 100644 --- a/FModel/Views/Resources/Converters/StringToGameConverter.cs +++ b/FModel/Views/Resources/Converters/StringToGameConverter.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Windows.Data; +using FModel.Extensions; namespace FModel.Views.Resources.Converters; @@ -10,28 +11,29 @@ public class StringToGameConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return value switch + var ret = value switch { - "Newt" => "Spellbreak", - "Nebula" => "Days Gone", - "Fortnite" => "Fortnite", - "VALORANT" => "Valorant", - "Pewee" => "Rogue Company", - "Catnip" => "Borderlands 3", - "AzaleaAlpha" => "The Cycle", - "Snoek" => "State of Decay 2", - "Rosemallow" => "The Outer Worlds", - "WorldExplorersLive" => "Battle Breakers", - "MinecraftDungeons" => "Minecraft Dungeons", - "shoebill" => "Star Wars: Jedi Fallen Order", - "a99769d95d8f400baad1f67ab5dfe508" => "Core", - "711c5e95dc094ca58e5f16bd48e751d6" => "That Banned Panda", - 381210 => "Dead By Daylight", - 578080 => "PLAYERUNKNOWN'S BATTLEGROUNDS", - 677620 => "Splitgate", - 1172620 => "Sea of Thieves", - _ => value, + "Newt" => FGame.g3, + "Nebula" => FGame.BendGame, + "Fortnite" => FGame.FortniteGame, + "VALORANT" => FGame.ShooterGame, + "Pewee" => FGame.RogueCompany, + "Catnip" => FGame.OakGame, + "AzaleaAlpha" => FGame.Prospect, + "Snoek" => FGame.StateOfDecay2, + "Rosemallow" => FGame.Indiana, + "WorldExplorersLive" => FGame.WorldExplorers, + "MinecraftDungeons" => FGame.Dungeons, + "shoebill" => FGame.SwGame, + "a99769d95d8f400baad1f67ab5dfe508" => FGame.Platform, + "711c5e95dc094ca58e5f16bd48e751d6" => FGame.PandaGame, + 381210 => FGame.DeadByDaylight, + 578080 => FGame.TslGame, + 677620 => FGame.PortalWars, + 1172620 => FGame.Athena, + _ => FGame.Unknown }; + return ret == FGame.Unknown ? value : ret.GetDescription(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) From 516fa33f937ba260559812674907efb259316959 Mon Sep 17 00:00:00 2001 From: 4sval Date: Sat, 6 Aug 2022 02:38:28 +0200 Subject: [PATCH 015/178] endpoint configuration template --- FModel/Creator/Bases/MV/BasePandaIcon.cs | 4 +- FModel/Enums.cs | 6 + FModel/Framework/FEndpoint.cs | 33 +++ FModel/Settings/UserSettings.cs | 59 ++++-- FModel/ViewModels/ApiEndpointViewModel.cs | 6 +- .../ApiEndpoints/DynamicApiEndpoint.cs | 47 +++++ .../{FModelApi.cs => FModelApiEndpoint.cs} | 4 +- .../FortniteCentralApiEndpoint.cs | 33 --- FModel/ViewModels/CUE4ParseViewModel.cs | 31 +-- FModel/ViewModels/SettingsViewModel.cs | 10 + FModel/Views/AesManager.xaml | 17 +- .../EndpointOverwriteToBoolConverter.cs | 52 +++++ .../Converters/EndpointToTypeConverter.cs | 32 +++ FModel/Views/SettingsView.xaml | 199 ++++++++---------- FModel/Views/SettingsView.xaml.cs | 9 +- 15 files changed, 349 insertions(+), 193 deletions(-) create mode 100644 FModel/Framework/FEndpoint.cs create mode 100644 FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs rename FModel/ViewModels/ApiEndpoints/{FModelApi.cs => FModelApiEndpoint.cs} (98%) create mode 100644 FModel/Views/Resources/Converters/EndpointOverwriteToBoolConverter.cs create mode 100644 FModel/Views/Resources/Converters/EndpointToTypeConverter.cs diff --git a/FModel/Creator/Bases/MV/BasePandaIcon.cs b/FModel/Creator/Bases/MV/BasePandaIcon.cs index a52a9bde..0f139b03 100644 --- a/FModel/Creator/Bases/MV/BasePandaIcon.cs +++ b/FModel/Creator/Bases/MV/BasePandaIcon.cs @@ -212,12 +212,12 @@ public class BasePandaIcon : UCreator return; var x = 450f; - var y = Height / 2 - DisplayNamePaint.TextSize / 4; - while (DisplayNamePaint.MeasureText(DisplayName) > Width - x) + while (DisplayNamePaint.MeasureText(DisplayName) > Width - x / 1.25) { DisplayNamePaint.TextSize -= 1; } + var y = Height / 2 - DisplayNamePaint.TextSize / 4; foreach (var a in DisplayName.Select(character => character.ToString())) { c.DrawText(a, x, y, DisplayNamePaint); diff --git a/FModel/Enums.cs b/FModel/Enums.cs index f5b29a98..b99682d6 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -141,3 +141,9 @@ public enum EIconStyle // [Description("Community")] // CommunityMade } + +public enum EEndpointType +{ + Aes, + Mapping +} diff --git a/FModel/Framework/FEndpoint.cs b/FModel/Framework/FEndpoint.cs new file mode 100644 index 00000000..f5edc115 --- /dev/null +++ b/FModel/Framework/FEndpoint.cs @@ -0,0 +1,33 @@ +namespace FModel.Framework; + +public class FEndpoint : ViewModel +{ + private string _url; + public string Url + { + get => _url; + set => SetProperty(ref _url, value); + } + + private bool _overwrite; + public bool Overwrite + { + get => _overwrite; + set => SetProperty(ref _overwrite, value); + } + + private string _path; + public string Path + { + get => _path; + set => SetProperty(ref _path, value); + } + + public bool IsEnabled => !string.IsNullOrWhiteSpace(_url); // change this later + + public FEndpoint() {} + public FEndpoint(string url) + { + Url = url; + } +} diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 82d238b1..640dce08 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -39,6 +39,16 @@ namespace FModel.Settings if (File.Exists(FilePath)) File.Delete(FilePath); } + public static bool TryGetGameCustomEndpoint(FGame game, EEndpointType type, out FEndpoint endpoint) + { + endpoint = null; + if (!Default.CustomEndpoints.TryGetValue(game, out var endpoints)) + return false; + + endpoint = endpoints[(int) type]; + return endpoint.IsEnabled; + } + private bool _showChangelog = true; public bool ShowChangelog { @@ -95,20 +105,6 @@ namespace FModel.Settings set => SetProperty(ref _gameDirectory, value); } - private bool _overwriteMapping; - public bool OverwriteMapping - { - get => _overwriteMapping; - set => SetProperty(ref _overwriteMapping, value); - } - - private string _mappingFilePath; - public string MappingFilePath - { - get => _mappingFilePath; - set => SetProperty(ref _mappingFilePath, value); - } - private int _lastOpenedSettingTab; public int LastOpenedSettingTab { @@ -382,6 +378,41 @@ namespace FModel.Settings set => SetProperty(ref _overridedOptions, value); } + private IDictionary _customEndpoints = new Dictionary + { + {FGame.Unknown, new FEndpoint[]{new (), new ()}}, + { + FGame.FortniteGame, new [] + { + new FEndpoint("https://fortnitecentral.gmatrixgames.ga/api/v1/aes"), + new FEndpoint("https://fortnitecentral.gmatrixgames.ga/api/v1/mappings") + } + }, + {FGame.ShooterGame, new FEndpoint[]{new (), new ()}}, + {FGame.DeadByDaylight, new FEndpoint[]{new (), new ()}}, + {FGame.OakGame, new FEndpoint[]{new (), new ()}}, + {FGame.Dungeons, new FEndpoint[]{new (), new ()}}, + {FGame.WorldExplorers, new FEndpoint[]{new (), new ()}}, + {FGame.g3, new FEndpoint[]{new (), new ()}}, + {FGame.StateOfDecay2, new FEndpoint[]{new (), new ()}}, + {FGame.Prospect, new FEndpoint[]{new (), new ()}}, + {FGame.Indiana, new FEndpoint[]{new (), new ()}}, + {FGame.RogueCompany, new FEndpoint[]{new (), new ()}}, + {FGame.SwGame, new FEndpoint[]{new (), new ()}}, + {FGame.Platform, new FEndpoint[]{new (), new ()}}, + {FGame.BendGame, new FEndpoint[]{new (), new ()}}, + {FGame.TslGame, new FEndpoint[]{new (), new ()}}, + {FGame.PortalWars, new FEndpoint[]{new (), new ()}}, + {FGame.Gameface, new FEndpoint[]{new (), new ()}}, + {FGame.Athena, new FEndpoint[]{new (), new ()}}, + {FGame.PandaGame, new FEndpoint[]{new (), new ()}} + }; + public IDictionary CustomEndpoints + { + get => _customEndpoints; + set => SetProperty(ref _customEndpoints, value); + } + private IDictionary> _customDirectories = new Dictionary> { {FGame.Unknown, new List()}, diff --git a/FModel/ViewModels/ApiEndpointViewModel.cs b/FModel/ViewModels/ApiEndpointViewModel.cs index f062266f..c880b891 100644 --- a/FModel/ViewModels/ApiEndpointViewModel.cs +++ b/FModel/ViewModels/ApiEndpointViewModel.cs @@ -22,7 +22,8 @@ public class ApiEndpointViewModel public ValorantApiEndpoint ValorantApi { get; } public FortniteCentralApiEndpoint CentralApi { get; } public EpicApiEndpoint EpicApi { get; } - public FModelApi FModelApi { get; } + public FModelApiEndpoint FModelApi { get; } + public DynamicApiEndpoint DynamicApi { get; } public ApiEndpointViewModel() { @@ -30,7 +31,8 @@ public class ApiEndpointViewModel ValorantApi = new ValorantApiEndpoint(_client); CentralApi = new FortniteCentralApiEndpoint(_client); EpicApi = new EpicApiEndpoint(_client); - FModelApi = new FModelApi(_client); + FModelApi = new FModelApiEndpoint(_client); + DynamicApi = new DynamicApiEndpoint(_client); } public async Task DownloadFileAsync(string fileLink, string installationPath) diff --git a/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs new file mode 100644 index 00000000..3c2244e5 --- /dev/null +++ b/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs @@ -0,0 +1,47 @@ +using System.Threading; +using System.Threading.Tasks; +using FModel.Framework; +using FModel.ViewModels.ApiEndpoints.Models; +using RestSharp; +using Serilog; + +namespace FModel.ViewModels.ApiEndpoints; + +public class DynamicApiEndpoint : AbstractApiProvider +{ + public DynamicApiEndpoint(RestClient client) : base(client) + { + } + + public async Task GetAesKeysAsync(CancellationToken token, string url) + { + var request = new FRestRequest(url) + { + OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } + }; + 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.Data; + } + + public AesResponse GetAesKeys(CancellationToken token, string url) + { + return GetAesKeysAsync(token, url).GetAwaiter().GetResult(); + } + + public async Task GetMappingsAsync(CancellationToken token, string url) + { + var request = new FRestRequest(url) + { + OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } + }; + 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.Data; + } + + public MappingsResponse[] GetMappings(CancellationToken token, string url) + { + return GetMappingsAsync(token, url).GetAwaiter().GetResult(); + } +} diff --git a/FModel/ViewModels/ApiEndpoints/FModelApi.cs b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs similarity index 98% rename from FModel/ViewModels/ApiEndpoints/FModelApi.cs rename to FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs index b0b5c2ba..bbd4ec09 100644 --- a/FModel/ViewModels/ApiEndpoints/FModelApi.cs +++ b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs @@ -20,7 +20,7 @@ using MessageBoxResult = AdonisUI.Controls.MessageBoxResult; namespace FModel.ViewModels.ApiEndpoints; -public class FModelApi : AbstractApiProvider +public class FModelApiEndpoint : AbstractApiProvider { private News _news; private Info _infos; @@ -29,7 +29,7 @@ public class FModelApi : AbstractApiProvider private readonly IDictionary _communityDesigns = new Dictionary(); private ApplicationViewModel _applicationView => ApplicationService.ApplicationView; - public FModelApi(RestClient client) : base(client) + public FModelApiEndpoint(RestClient client) : base(client) { } diff --git a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs index 2ebb7d22..e5fee91a 100644 --- a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs @@ -2,7 +2,6 @@ using System.Threading; using System.Threading.Tasks; using FModel.Framework; -using FModel.ViewModels.ApiEndpoints.Models; using RestSharp; using Serilog; @@ -14,38 +13,6 @@ public class FortniteCentralApiEndpoint : AbstractApiProvider { } - public async Task GetAesKeysAsync(CancellationToken token) - { - var request = new FRestRequest("https://fortnitecentral.gmatrixgames.ga/api/v1/aes") - { - OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } - }; - 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.Data; - } - - public AesResponse GetAesKeys(CancellationToken token) - { - return GetAesKeysAsync(token).GetAwaiter().GetResult(); - } - - public async Task GetMappingsAsync(CancellationToken token) - { - var request = new FRestRequest("https://fortnitecentral.gmatrixgames.ga/api/v1/mappings") - { - OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; } - }; - 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.Data; - } - - public MappingsResponse[] GetMappings(CancellationToken token) - { - return GetMappingsAsync(token).GetAwaiter().GetResult(); - } - public async Task>> GetHotfixesAsync(CancellationToken token, string language = "en") { var request = new FRestRequest("https://fortnitecentral.gmatrixgames.ga/api/v1/hotfixes") diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index c1eeb273..2ba50435 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -293,16 +293,18 @@ public class CUE4ParseViewModel : ViewModel public async Task RefreshAes() { - if (Game == FGame.FortniteGame) // game directory dependent, we don't have the provider game name yet since we don't have aes keys - { - await _threadWorkerView.Begin(cancellationToken => - { - var aes = _apiEndpointView.CentralApi.GetAesKeys(cancellationToken); - if (aes?.MainKey == null && aes?.DynamicKeys == null && aes?.Version == null) return; + // game directory dependent, we don't have the provider game name yet since we don't have aes keys + // except when this comes from the AES Manager + if (!UserSettings.TryGetGameCustomEndpoint(Game, EEndpointType.Aes, out var endpoint)) + return; - UserSettings.Default.AesKeys[Game] = aes; - }); - } + await _threadWorkerView.Begin(cancellationToken => + { + var aes = _apiEndpointView.DynamicApi.GetAesKeys(cancellationToken, endpoint.Url); + if (aes?.MainKey == null && aes?.DynamicKeys == null) return; + + UserSettings.Default.AesKeys[Game] = aes; + }); } public async Task InitInformation() @@ -321,20 +323,21 @@ public class CUE4ParseViewModel : ViewModel public async Task InitBenMappings() { - if (Game != FGame.FortniteGame) return; + if (!UserSettings.TryGetGameCustomEndpoint(Game, EEndpointType.Mapping, out var endpoint)) + return; await _threadWorkerView.Begin(cancellationToken => { - if (UserSettings.Default.OverwriteMapping && File.Exists(UserSettings.Default.MappingFilePath)) + if (endpoint.Overwrite && File.Exists(endpoint.Path)) { - Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(UserSettings.Default.MappingFilePath); + Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(endpoint.Path); FLogger.AppendInformation(); - FLogger.AppendText($"Mappings pulled from '{UserSettings.Default.MappingFilePath.SubstringAfterLast("\\")}'", Constants.WHITE, true); + FLogger.AppendText($"Mappings pulled from '{endpoint.Path.SubstringAfterLast("\\")}'", Constants.WHITE, true); } else { var mappingsFolder = Path.Combine(UserSettings.Default.OutputDirectory, ".data"); - var mappings = _apiEndpointView.CentralApi.GetMappings(cancellationToken); + var mappings = _apiEndpointView.DynamicApi.GetMappings(cancellationToken, endpoint.Url); if (mappings is { Length: > 0 }) { foreach (var mapping in mappings) diff --git a/FModel/ViewModels/SettingsViewModel.cs b/FModel/ViewModels/SettingsViewModel.cs index 99d5171d..3d12be19 100644 --- a/FModel/ViewModels/SettingsViewModel.cs +++ b/FModel/ViewModels/SettingsViewModel.cs @@ -96,6 +96,13 @@ public class SettingsViewModel : ViewModel set => SetProperty(ref _selectedCompressedAudio, value); } + private string _currentMappingFile; + public string CurrentMappingFile // only used so that it updates the UI + { + get => _currentMappingFile; + set => SetProperty(ref _currentMappingFile, value); + } + private EIconStyle _selectedCosmeticStyle; public EIconStyle SelectedCosmeticStyle { @@ -191,6 +198,9 @@ public class SettingsViewModel : ViewModel _optionsSnapshot = UserSettings.Default.OverridedOptions[_game]; } + if (UserSettings.TryGetGameCustomEndpoint(_game, EEndpointType.Mapping, out var endpoint)) + CurrentMappingFile = endpoint.Path; + _assetLanguageSnapshot = UserSettings.Default.AssetLanguage; _compressedAudioSnapshot = UserSettings.Default.CompressedAudioMode; _cosmeticStyleSnapshot = UserSettings.Default.CosmeticStyle; diff --git a/FModel/Views/AesManager.xaml b/FModel/Views/AesManager.xaml index 8b5e8429..442fd17c 100644 --- a/FModel/Views/AesManager.xaml +++ b/FModel/Views/AesManager.xaml @@ -81,16 +81,13 @@ HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="OK" Click="OnClick" /> diff --git a/FModel/Views/Resources/Converters/EndpointOverwriteToBoolConverter.cs b/FModel/Views/Resources/Converters/EndpointOverwriteToBoolConverter.cs new file mode 100644 index 00000000..6fdd553c --- /dev/null +++ b/FModel/Views/Resources/Converters/EndpointOverwriteToBoolConverter.cs @@ -0,0 +1,52 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using FModel.Framework; +using FModel.Settings; +using FModel.ViewModels; + +namespace FModel.Views.Resources.Converters; + +public class EndpointOverwriteToBoolConverter : IMultiValueConverter +{ + public static readonly EndpointOverwriteToBoolConverter Instance = new(); + + private ApplicationViewModel _vm; + private EEndpointType _type; + private FEndpoint _endpoint; + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + _vm = values[0] as ApplicationViewModel; + _type = values[1] as EEndpointType? ?? EEndpointType.Mapping; + if (_vm == null || !UserSettings.TryGetGameCustomEndpoint(_vm.CUE4Parse.Game, _type, out _endpoint)) + return default; + + return targetType switch + { + not null when targetType == typeof(bool?) => _endpoint.Overwrite, // IsChecked + not null when targetType == typeof(string) => _endpoint.Path, + not null when targetType == typeof(Visibility) => _endpoint.Overwrite ? Visibility.Visible : Visibility.Collapsed, + _ => throw new NotImplementedException() + }; + } + + public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) + { + var t = value.GetType(); + switch (t) + { + case not null when t == typeof(bool): + _endpoint.Overwrite = (bool)value; + break; + case not null when t == typeof(string): + _endpoint.Path = (string)value; + break; + default: + throw new NotImplementedException(); + } + + return new object[] { _vm, _type }; + } +} diff --git a/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs b/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs new file mode 100644 index 00000000..730a8d22 --- /dev/null +++ b/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using FModel.Settings; +using FModel.ViewModels; + +namespace FModel.Views.Resources.Converters; + +public class EndpointToTypeConverter : IMultiValueConverter +{ + public static readonly EndpointToTypeConverter Instance = new(); + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values[0] is not ApplicationViewModel viewModel || + values[1] is not EEndpointType type) + return false; + + var isEnabled = UserSettings.TryGetGameCustomEndpoint(viewModel.CUE4Parse.Game, type, out _); + return targetType switch + { + not null when targetType == typeof(Visibility) => isEnabled ? Visibility.Visible : Visibility.Collapsed, + _ => throw new NotImplementedException() + }; + } + + public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index 39b43582..0a9ffa2d 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -41,6 +41,7 @@ + @@ -130,17 +131,6 @@ DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" SelectionChanged="OnSelectionChanged" Margin="0 0 0 5"> - - - - - - - - - + + + + + + + + + @@ -174,31 +175,47 @@ - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + + + + @@ -206,90 +223,46 @@ - - + - - - - + + + + + + + + - - - - + + + + + + + + + + + + + + + - - - - + - - - - - - + + - - - - - - - - + - - - - - - + ItemsSource="{Binding SettingsView.AesReloads}" SelectedItem="{Binding SettingsView.SelectedAesReload, Mode=TwoWay}" + Visibility="{Binding SettingsView.AesEndpoint.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"> @@ -227,42 +217,25 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" + Visibility="{Binding SettingsView.MappingEndpoint.Overwrite, Converter={StaticResource BoolToVisibilityConverter}}"> - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FModel/Views/ModelViewer.xaml.cs b/FModel/Views/ModelViewer.xaml.cs deleted file mode 100644 index 07d6a2bd..00000000 --- a/FModel/Views/ModelViewer.xaml.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.ComponentModel; -using System.Windows; -using System.Windows.Input; -using AdonisUI.Controls; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Material; -using FModel.Services; -using FModel.ViewModels; -using HelixToolkit.Wpf.SharpDX; -using MessageBox = AdonisUI.Controls.MessageBox; -using MessageBoxImage = AdonisUI.Controls.MessageBoxImage; - -namespace FModel.Views; - -public partial class ModelViewer -{ - private bool _messageShown; - private ApplicationViewModel _applicationView => ApplicationService.ApplicationView; - - public ModelViewer() - { - DataContext = _applicationView; - InitializeComponent(); - } - - public void Load(UObject export) => _applicationView.ModelViewer.LoadExport(export); - public void Overwrite(UMaterialInstance materialInstance) - { - if (_applicationView.ModelViewer.TryOverwriteMaterial(materialInstance)) - { - _applicationView.CUE4Parse.ModelIsOverwritingMaterial = false; - } - else - { - MessageBox.Show(new MessageBoxModel - { - Text = "An attempt to load a material failed.", - Caption = "Error", - Icon = MessageBoxImage.Error, - Buttons = MessageBoxButtons.OkCancel(), - IsSoundEnabled = false - }); - } - } - - private void OnClosing(object sender, CancelEventArgs e) - { - _applicationView.ModelViewer.Clear(); - _applicationView.ModelViewer.AppendMode = false; - _applicationView.CUE4Parse.ModelIsOverwritingMaterial = false; - MyAntiCrashGroup.ItemsSource = null; // <3 - } - - private async void OnWindowKeyDown(object sender, KeyEventArgs e) - { - switch (e.Key) - { - case Key.W: - _applicationView.ModelViewer.WirefreameToggle(); - break; - case Key.H: - _applicationView.ModelViewer.RenderingToggle(); - break; - case Key.D: - _applicationView.ModelViewer.DiffuseOnlyToggle(); - break; - case Key.M: - _applicationView.ModelViewer.MaterialColorToggle(); - break; - case Key.Decimal: - _applicationView.ModelViewer.FocusOnSelectedMesh(); - break; - case Key.S when Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift): - _applicationView.ModelViewer.SaveAsScene(); - break; - case Key.S when Keyboard.Modifiers.HasFlag(ModifierKeys.Control): - await _applicationView.ModelViewer.SaveLoadedModels(); - break; - } - } - - private void OnMouse3DDown(object sender, MouseDown3DEventArgs e) - { - if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || e.HitTestResult.ModelHit is not CustomMeshGeometryModel3D m) return; - _applicationView.ModelViewer.SelectedModel.SelectedGeometry = m; - MaterialsListName.ScrollIntoView(m); - } - - private void OnFocusClick(object sender, RoutedEventArgs e) - => _applicationView.ModelViewer.FocusOnSelectedMesh(); - - private void OnCopyClick(object sender, RoutedEventArgs e) - => _applicationView.ModelViewer.CopySelectedMaterialName(); - - private async void Save(object sender, RoutedEventArgs e) - => await _applicationView.ModelViewer.SaveLoadedModels(); - - private void OnOverwriteMaterialClick(object sender, RoutedEventArgs e) - { - _applicationView.CUE4Parse.ModelIsOverwritingMaterial = true; - if (!_messageShown) - { - MessageBox.Show(new MessageBoxModel - { - Text = "Simply extract a material once FModel will be brought to the foreground. This message will be shown once per Model Viewer's lifetime, close it to begin.", - Caption = "How To Overwrite Material?", - Icon = MessageBoxImage.Information, - IsSoundEnabled = false - }); - _messageShown = true; - } - - MainWindow.YesWeCats.Activate(); - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Converters/BoolToFillModeConverter.cs b/FModel/Views/Resources/Converters/BoolToFillModeConverter.cs deleted file mode 100644 index 3a85a8c7..00000000 --- a/FModel/Views/Resources/Converters/BoolToFillModeConverter.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using SharpDX.Direct3D11; - -namespace FModel.Views.Resources.Converters; - -public class BoolToFillModeConverter : IValueConverter -{ - public static readonly BoolToFillModeConverter Instance = new(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - FillMode.Solid => true, - _ => false - }; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - true => FillMode.Solid, - _ => FillMode.Wireframe - }; - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Converters/DateTimeToStringConverter.cs b/FModel/Views/Resources/Converters/DateTimeToStringConverter.cs deleted file mode 100644 index 1d525940..00000000 --- a/FModel/Views/Resources/Converters/DateTimeToStringConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace FModel.Views.Resources.Converters; - -public class DateTimeToStringConverter : IValueConverter -{ - public static readonly DateTimeToStringConverter Instance = new(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is not DateTime dateTime ? value : $"{dateTime.ToLongDateString()}, {dateTime.ToShortTimeString()}"; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Converters/TagToColorConverter.cs b/FModel/Views/Resources/Converters/TagToColorConverter.cs deleted file mode 100644 index ae0d4cfd..00000000 --- a/FModel/Views/Resources/Converters/TagToColorConverter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using System.Windows.Media; -using HelixToolkit.Wpf.SharpDX; - -namespace FModel.Views.Resources.Converters; - -public class TagToColorConverter : IValueConverter -{ - public static readonly TagToColorConverter Instance = new(); - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is not PBRMaterial material) - return new SolidColorBrush(Colors.Red); - - return new SolidColorBrush(Color.FromScRgb( - material.AlbedoColor.Alpha, material.AlbedoColor.Red, - material.AlbedoColor.Green, material.AlbedoColor.Blue)); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index 984f2454..3dd838e2 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -4,7 +4,6 @@ xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:controls="clr-namespace:FModel.Views.Resources.Controls" xmlns:soundOut="clr-namespace:CSCore.SoundOut;assembly=CSCore" - xmlns:sharpDx="clr-namespace:SharpDX.Direct3D11;assembly=SharpDX.Direct3D11" xmlns:audioControls="clr-namespace:FModel.Views.Resources.Controls.Aup" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit" @@ -638,91 +637,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index 1e782c99..ed12174e 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -95,10 +95,10 @@ public partial class MainWindow if (_threadWorkerView.CanBeCanceled && e.Key == Key.Escape) { - _applicationView.Status = EStatusKind.Stopping; + _applicationView.Status.SetStatus(EStatusKind.Stopping); _threadWorkerView.Cancel(); } - else if (_applicationView.IsReady && e.Key == Key.F && Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) + else if (_applicationView.Status.IsReady && e.Key == Key.F && Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) OnSearchViewClick(null, null); else if (e.Key == Key.Left && _applicationView.CUE4Parse.TabControl.SelectedTab.HasImage) _applicationView.CUE4Parse.TabControl.SelectedTab.GoPreviousImage(); @@ -230,14 +230,14 @@ public partial class MainWindow private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e) { - if (!_applicationView.IsReady || sender is not ListBox listBox) return; + if (!_applicationView.Status.IsReady || sender is not ListBox listBox) return; UserSettings.Default.LoadingMode = ELoadingMode.Multiple; _applicationView.LoadingModes.LoadCommand.Execute(listBox.SelectedItems); } private async void OnPreviewKeyDown(object sender, KeyEventArgs e) { - if (!_applicationView.IsReady || sender is not ListBox listBox) return; + if (!_applicationView.Status.IsReady || sender is not ListBox listBox) return; switch (e.Key) { diff --git a/FModel/Settings/UserSettings.cs b/FModel/Settings/UserSettings.cs index 7be5c4b1..1689364d 100644 --- a/FModel/Settings/UserSettings.cs +++ b/FModel/Settings/UserSettings.cs @@ -46,7 +46,7 @@ namespace FModel.Settings return false; endpoint = endpoints[(int) type]; - return endpoint.IsValid; + return endpoint.Overwrite || endpoint.IsValid; } private bool _showChangelog = true; diff --git a/FModel/ViewModels/ApplicationViewModel.cs b/FModel/ViewModels/ApplicationViewModel.cs index 7438c6a6..9c3a1c2d 100644 --- a/FModel/ViewModels/ApplicationViewModel.cs +++ b/FModel/ViewModels/ApplicationViewModel.cs @@ -22,7 +22,6 @@ namespace FModel.ViewModels; public class ApplicationViewModel : ViewModel { private EBuildKind _build; - public EBuildKind Build { get => _build; @@ -33,24 +32,11 @@ public class ApplicationViewModel : ViewModel } } - private bool _isReady; - - public bool IsReady - { - get => _isReady; - private set => SetProperty(ref _isReady, value); - } - - private EStatusKind _status; - - public EStatusKind Status + private FStatus _status; + public FStatus Status { get => _status; - set - { - SetProperty(ref _status, value); - IsReady = Status != EStatusKind.Loading && Status != EStatusKind.Stopping; - } + set => SetProperty(ref _status, value); } public RightClickMenuCommand RightClickMenuCommand => _rightClickMenuCommand ??= new RightClickMenuCommand(this); @@ -76,7 +62,7 @@ public class ApplicationViewModel : ViewModel public ApplicationViewModel() { - Status = EStatusKind.Loading; + Status = new FStatus(); #if DEBUG Build = EBuildKind.Debug; #elif RELEASE @@ -93,7 +79,7 @@ public class ApplicationViewModel : ViewModel AesManager = new AesManagerViewModel(CUE4Parse); MapViewer = new MapViewerViewModel(CUE4Parse); AudioPlayer = new AudioPlayerViewModel(); - Status = EStatusKind.Ready; + Status.SetStatus(EStatusKind.Ready); } public void AvoidEmptyGameDirectoryAndSetEGame(bool bAlreadyLaunched) diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 2203df3f..793b10dc 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -56,7 +56,6 @@ public class CUE4ParseViewModel : ViewModel RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); private FGame _game; - public FGame Game { get => _game; @@ -458,7 +457,7 @@ public class CUE4ParseViewModel : ViewModel cancellationToken.ThrowIfCancellationRequested(); try { - Extract(asset.FullPath, TabControl.HasNoTabs); + Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs); } catch { @@ -489,7 +488,7 @@ public class CUE4ParseViewModel : ViewModel cancellationToken.ThrowIfCancellationRequested(); try { - Extract(asset.FullPath, TabControl.HasNoTabs, true); + Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, true); } catch { @@ -506,11 +505,11 @@ public class CUE4ParseViewModel : ViewModel { Thread.Sleep(10); cancellationToken.ThrowIfCancellationRequested(); - Extract(asset.FullPath, TabControl.HasNoTabs); + Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs); } } - public void Extract(string fullPath, bool addNewTab = false, bool bulkSave = false) + public void Extract(CancellationToken cancellationToken, string fullPath, bool addNewTab = false, bool bulkSave = false) { Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", fullPath); @@ -543,7 +542,7 @@ public class CUE4ParseViewModel : ViewModel foreach (var e in exports) { - if (CheckExport(e)) + if (CheckExport(cancellationToken, e)) break; } @@ -704,7 +703,7 @@ public class CUE4ParseViewModel : ViewModel } } - public void ExtractAndScroll(string fullPath, string objectName) + public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName) { Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath); TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/')); @@ -716,12 +715,12 @@ public class CUE4ParseViewModel : ViewModel foreach (var e in exports) { - if (CheckExport(e)) + if (CheckExport(cancellationToken, e)) break; } } - private bool CheckExport(UObject export) // return true once you wanna stop searching for exports + private bool CheckExport(CancellationToken cancellationToken, UObject export) // return true once you wanna stop searching for exports { switch (export) { @@ -758,7 +757,7 @@ public class CUE4ParseViewModel : ViewModel export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) || export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))): { - SnooperViewer.Run(export); + SnooperViewer.Run(cancellationToken, export); return true; } case UMaterialInstance m when ModelIsOverwritingMaterial: diff --git a/FModel/ViewModels/Commands/LoadCommand.cs b/FModel/ViewModels/Commands/LoadCommand.cs index e8bc4f7d..3e26ccc4 100644 --- a/FModel/ViewModels/Commands/LoadCommand.cs +++ b/FModel/ViewModels/Commands/LoadCommand.cs @@ -105,8 +105,7 @@ public class LoadCommand : ViewModelCommand private void FilterDirectoryFilesToDisplay(CancellationToken cancellationToken, IEnumerable directoryFiles) { HashSet filter; - if (directoryFiles == null) - filter = null; + if (directoryFiles == null) filter = null; else { filter = new HashSet(); diff --git a/FModel/ViewModels/Commands/RightClickMenuCommand.cs b/FModel/ViewModels/Commands/RightClickMenuCommand.cs index ff41ba8e..0e2f6dcc 100644 --- a/FModel/ViewModels/Commands/RightClickMenuCommand.cs +++ b/FModel/ViewModels/Commands/RightClickMenuCommand.cs @@ -29,7 +29,7 @@ public class RightClickMenuCommand : ViewModelCommand foreach (var asset in assetItems) { cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(asset.FullPath, true); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, true); } break; @@ -45,7 +45,7 @@ public class RightClickMenuCommand : ViewModelCommand foreach (var asset in assetItems) { cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(asset.FullPath); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath); contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveProperty(false); } @@ -54,7 +54,7 @@ public class RightClickMenuCommand : ViewModelCommand foreach (var asset in assetItems) { cancellationToken.ThrowIfCancellationRequested(); - contextViewModel.CUE4Parse.Extract(asset.FullPath); + contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath); contextViewModel.CUE4Parse.TabControl.SelectedTab.SaveImage(false); } @@ -62,4 +62,4 @@ public class RightClickMenuCommand : ViewModelCommand } }); } -} \ No newline at end of file +} diff --git a/FModel/ViewModels/ThreadWorkerViewModel.cs b/FModel/ViewModels/ThreadWorkerViewModel.cs index 2f8fc49f..dc6ad853 100644 --- a/FModel/ViewModels/ThreadWorkerViewModel.cs +++ b/FModel/ViewModels/ThreadWorkerViewModel.cs @@ -53,7 +53,7 @@ public class ThreadWorkerViewModel : ViewModel public async Task Begin(Action action) { - if (!_applicationView.IsReady) + if (!_applicationView.Status.IsReady) { SignalOperationInProgress(); return; @@ -79,7 +79,7 @@ public class ThreadWorkerViewModel : ViewModel { if (_jobs.Count > 0) { - _applicationView.Status = EStatusKind.Loading; + _applicationView.Status.SetStatus(EStatusKind.Loading); await foreach (var job in _jobs) { try @@ -89,7 +89,7 @@ public class ThreadWorkerViewModel : ViewModel } catch (OperationCanceledException) { - _applicationView.Status = EStatusKind.Stopped; + _applicationView.Status.SetStatus(EStatusKind.Stopped); CurrentCancellationTokenSource = null; // kill token OperationCancelled = true; OperationCancelled = false; @@ -97,7 +97,7 @@ public class ThreadWorkerViewModel : ViewModel } catch (Exception e) { - _applicationView.Status = EStatusKind.Failed; + _applicationView.Status.SetStatus(EStatusKind.Failed); CurrentCancellationTokenSource = null; // kill token Log.Error("{Exception}", e); @@ -134,7 +134,7 @@ public class ThreadWorkerViewModel : ViewModel } } - _applicationView.Status = EStatusKind.Completed; + _applicationView.Status.SetStatus(EStatusKind.Completed); CurrentCancellationTokenSource = null; // kill token } } diff --git a/FModel/Views/MapViewer.xaml b/FModel/Views/MapViewer.xaml index 34b57e04..54de3a50 100644 --- a/FModel/Views/MapViewer.xaml +++ b/FModel/Views/MapViewer.xaml @@ -24,41 +24,41 @@ + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> - + + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> - + + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> + DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding Status.IsReady}" /> @@ -108,7 +108,7 @@ -