not bullet proof but good enough

This commit is contained in:
4sval 2022-08-07 04:50:39 +02:00
parent a8c60ac3a9
commit 947d5011c2
14 changed files with 256 additions and 166 deletions

View File

@ -1,4 +1,9 @@
namespace FModel.Framework;
using System.Linq;
using FModel.ViewModels.ApiEndpoints;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace FModel.Framework;
public class FEndpoint : ViewModel
{
@ -6,18 +11,7 @@ public class FEndpoint : ViewModel
public string Url
{
get => _url;
set
{
SetProperty(ref _url, value);
RaisePropertyChanged(nameof(IsEnabled));
}
}
private bool _overwrite;
public bool Overwrite
{
get => _overwrite;
set => SetProperty(ref _overwrite, value);
set => SetProperty(ref _url, value);
}
private string _path;
@ -27,11 +21,72 @@ public class FEndpoint : ViewModel
set => SetProperty(ref _path, value);
}
public bool IsEnabled => !string.IsNullOrWhiteSpace(_url); // change this later
private bool _overwrite;
public bool Overwrite
{
get => _overwrite;
set => SetProperty(ref _overwrite, value);
}
private string _filePath;
public string FilePath
{
get => _filePath;
set => SetProperty(ref _filePath, value);
}
private bool _isValid;
public bool IsValid
{
get => _isValid;
set
{
SetProperty(ref _isValid, value);
RaisePropertyChanged(nameof(Label));
}
}
[JsonIgnore]
public string Label => IsValid ?
"Your endpoint configuration is valid! Please, avoid any unnecessary modifications!" :
"Your endpoint configuration DOES NOT seem to be valid yet! Please, test it out!";
public FEndpoint() {}
public FEndpoint(string url)
public FEndpoint(string url, string path)
{
Url = url;
Path = path;
IsValid = !string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(path); // be careful with this
}
public void TryValidate(DynamicApiEndpoint endpoint, EEndpointType type, out JToken response)
{
response = null;
if (string.IsNullOrEmpty(Url) || string.IsNullOrEmpty(Path))
{
IsValid = false;
}
else switch (type)
{
case EEndpointType.Aes:
{
var r = endpoint.GetAesKeys(default, Url, Path);
response = JToken.FromObject(r);
IsValid = r.IsValid;
break;
}
case EEndpointType.Mapping:
{
var r = endpoint.GetMappings(default, Url, Path);
response = JToken.FromObject(r);
IsValid = r.Any(x => x.IsValid);
break;
}
default:
{
IsValid = false;
break;
}
}
}
}

View File

@ -21,6 +21,17 @@ public static class Helper
key.StartsWith("mu", StringComparison.OrdinalIgnoreCase) &&
key.EndsWith("sus", StringComparison.OrdinalIgnoreCase);
public static string FixKey(string key)
{
if (string.IsNullOrEmpty(key))
return string.Empty;
if (key.StartsWith("0x"))
key = key[2..];
return "0x" + key.ToUpper().Trim();
}
public static void OpenWindow<T>(string windowName, Action action) where T : Window
{
if (!IsWindowOpen<T>(windowName))

View File

@ -39,14 +39,14 @@ namespace FModel.Settings
if (File.Exists(FilePath)) File.Delete(FilePath);
}
public static bool IsEndpointEnabled(FGame game, EEndpointType type, out FEndpoint endpoint)
public static bool IsEndpointValid(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;
return endpoint.IsValid;
}
private bool _showChangelog = true;
@ -384,8 +384,8 @@ namespace FModel.Settings
{
FGame.FortniteGame, new []
{
new FEndpoint("https://fortnitecentral.gmatrixgames.ga/api/v1/aes"),
new FEndpoint("https://fortnitecentral.gmatrixgames.ga/api/v1/mappings")
new FEndpoint("https://fortnitecentral.gmatrixgames.ga/api/v1/aes", "$.['mainKey','dynamicKeys']"),
new FEndpoint("https://fortnitecentral.gmatrixgames.ga/api/v1/mappings", "$.[?(@.meta.compressionMethod=='Oodle')].['url','fileName']")
}
},
{FGame.ShooterGame, new FEndpoint[]{new (), new ()}},

View File

@ -51,7 +51,7 @@ public class AesManagerViewModel : ViewModel
DynamicKeys = null
};
_mainKey.Key = FixKey(_keysFromSettings.MainKey);
_mainKey.Key = Helper.FixKey(_keysFromSettings.MainKey);
AesKeys = new FullyObservableCollection<FileItem>(EnumerateAesKeys());
AesKeys.ItemPropertyChanged += AesKeysOnItemPropertyChanged;
AesKeysView = new ListCollectionView(AesKeys) { SortDescriptions = { new SortDescription("Name", ListSortDirection.Ascending) } };
@ -63,11 +63,11 @@ public class AesManagerViewModel : ViewModel
if (e.PropertyName != "Key" || sender is not FullyObservableCollection<FileItem> collection)
return;
var key = FixKey(collection[e.CollectionIndex].Key);
var key = Helper.FixKey(collection[e.CollectionIndex].Key);
if (e.CollectionIndex == 0)
{
if (!HasChange)
HasChange = FixKey(_keysFromSettings.MainKey) != key;
HasChange = Helper.FixKey(_keysFromSettings.MainKey) != key;
_keysFromSettings.MainKey = key;
}
@ -87,7 +87,7 @@ public class AesManagerViewModel : ViewModel
else if (_keysFromSettings.DynamicKeys.FirstOrDefault(x => x.Guid == collection[e.CollectionIndex].Guid.ToString()) is { } d)
{
if (!HasChange)
HasChange = FixKey(d.Key) != key;
HasChange = Helper.FixKey(d.Key) != key;
d.Key = key;
}
@ -117,17 +117,6 @@ public class AesManagerViewModel : ViewModel
Log.Information("{@Json}", UserSettings.Default);
}
private string FixKey(string key)
{
if (string.IsNullOrEmpty(key))
return string.Empty;
if (key.StartsWith("0x"))
key = key[2..];
return "0x" + key.ToUpper().Trim();
}
private IEnumerable<FileItem> EnumerateAesKeys()
{
yield return _mainKey;
@ -145,7 +134,7 @@ public class AesManagerViewModel : ViewModel
k = dynamicKey.Key;
}
file.Key = FixKey(k);
file.Key = Helper.FixKey(k);
yield return file;
}
}

View File

@ -1,7 +1,9 @@
using System.Threading;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FModel.Framework;
using FModel.ViewModels.ApiEndpoints.Models;
using Newtonsoft.Json.Linq;
using RestSharp;
using Serilog;
@ -13,35 +15,55 @@ public class DynamicApiEndpoint : AbstractApiProvider
{
}
public async Task<AesResponse> GetAesKeysAsync(CancellationToken token, string url)
public async Task<AesResponse> GetAesKeysAsync(CancellationToken token, string url, string path)
{
var request = new FRestRequest(url)
{
OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; }
};
var response = await _client.ExecuteAsync<AesResponse>(request, token).ConfigureAwait(false);
var response = await _client.ExecuteAsync(request, token).ConfigureAwait(false);
var body = JToken.Parse(response.Content!);
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, response.ResponseUri?.OriginalString);
return response.Data;
var tokens = body.SelectTokens(path);
var ret = new AesResponse { MainKey = Helper.FixKey(tokens.ElementAtOrDefault(0).ToString()) };
if (tokens.ElementAtOrDefault(1) is JArray dynamicKeys)
{
foreach (var dynamicKey in dynamicKeys)
{
if (dynamicKey["guid"] is not { } guid || dynamicKey["key"] is not { } key)
continue;
ret.DynamicKeys.Add(new DynamicKey{Guid = guid.ToString(), Key = Helper.FixKey(key.ToString())});
}
}
return ret;
}
public AesResponse GetAesKeys(CancellationToken token, string url)
public AesResponse GetAesKeys(CancellationToken token, string url, string path)
{
return GetAesKeysAsync(token, url).GetAwaiter().GetResult();
return GetAesKeysAsync(token, url, path).GetAwaiter().GetResult();
}
public async Task<MappingsResponse[]> GetMappingsAsync(CancellationToken token, string url)
public async Task<MappingsResponse[]> GetMappingsAsync(CancellationToken token, string url, string path)
{
var request = new FRestRequest(url)
{
OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; }
};
var response = await _client.ExecuteAsync<MappingsResponse[]>(request, token).ConfigureAwait(false);
var response = await _client.ExecuteAsync(request, token).ConfigureAwait(false);
var body = JToken.Parse(response.Content!);
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, response.ResponseUri?.OriginalString);
return response.Data;
var tokens = body.SelectTokens(path);
var ret = new MappingsResponse[] {new()};
ret[0].Url = tokens.ElementAtOrDefault(0).ToString();
ret[0].FileName = tokens.ElementAtOrDefault(1).ToString();
return ret;
}
public MappingsResponse[] GetMappings(CancellationToken token, string url)
public MappingsResponse[] GetMappings(CancellationToken token, string url, string path)
{
return GetMappingsAsync(token, url).GetAwaiter().GetResult();
return GetMappingsAsync(token, url, path).GetAwaiter().GetResult();
}
}

View File

@ -19,7 +19,7 @@ public class AesResponse
}
[I] public bool HasDynamicKeys => DynamicKeys is { Count: > 0 };
[I] public bool IsValid => !string.IsNullOrEmpty(MainKey);
[I] public bool IsValid => MainKey.Length == 66;
}
[DebuggerDisplay("{" + nameof(Key) + "}")]
@ -29,6 +29,5 @@ public class DynamicKey
[J("guid")] public string Guid { get; set; }
[J("key")] public string Key { get; set; }
[I] public bool IsValid => !string.IsNullOrEmpty(Guid) &&
!string.IsNullOrEmpty(Key);
[I] public bool IsValid => Guid.Length == 32 && Key.Length == 66;
}

View File

@ -22,8 +22,7 @@ public class MappingsResponse
}
[I] public bool IsValid => !string.IsNullOrEmpty(Url) &&
!string.IsNullOrEmpty(FileName) &&
Meta != null;
!string.IsNullOrEmpty(FileName);
}
[DebuggerDisplay("{" + nameof(CompressionMethod) + "}")]
@ -31,11 +30,4 @@ public class Meta
{
[I][J] public string Version { get; private set; }
[J] public string CompressionMethod { get; set; }
public Meta()
{
CompressionMethod = "Oodle";
}
[I] public bool IsValid => CompressionMethod == "Oodle";
}

View File

@ -295,12 +295,12 @@ public class CUE4ParseViewModel : ViewModel
{
// 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.IsEndpointEnabled(Game, EEndpointType.Aes, out var endpoint))
if (!UserSettings.IsEndpointValid(Game, EEndpointType.Aes, out var endpoint))
return;
await _threadWorkerView.Begin(cancellationToken =>
{
var aes = _apiEndpointView.DynamicApi.GetAesKeys(cancellationToken, endpoint.Url);
var aes = _apiEndpointView.DynamicApi.GetAesKeys(cancellationToken, endpoint.Url, endpoint.Path);
if (aes is not { IsValid: true }) return;
UserSettings.Default.AesKeys[Game] = aes;
@ -323,26 +323,26 @@ public class CUE4ParseViewModel : ViewModel
public async Task InitBenMappings()
{
if (!UserSettings.IsEndpointEnabled(Game, EEndpointType.Mapping, out var endpoint))
if (!UserSettings.IsEndpointValid(Game, EEndpointType.Mapping, out var endpoint))
return;
await _threadWorkerView.Begin(cancellationToken =>
{
if (endpoint.Overwrite && File.Exists(endpoint.Path))
if (endpoint.Overwrite && File.Exists(endpoint.FilePath))
{
Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(endpoint.Path);
Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(endpoint.FilePath);
FLogger.AppendInformation();
FLogger.AppendText($"Mappings pulled from '{endpoint.Path.SubstringAfterLast("\\")}'", Constants.WHITE, true);
FLogger.AppendText($"Mappings pulled from '{endpoint.FilePath.SubstringAfterLast("\\")}'", Constants.WHITE, true);
}
else
{
var mappingsFolder = Path.Combine(UserSettings.Default.OutputDirectory, ".data");
var mappings = _apiEndpointView.DynamicApi.GetMappings(cancellationToken, endpoint.Url);
var mappings = _apiEndpointView.DynamicApi.GetMappings(cancellationToken, endpoint.Url, endpoint.Path);
if (mappings is { Length: > 0 })
{
foreach (var mapping in mappings)
{
if (!mapping.IsValid || !mapping.Meta.IsValid) continue;
if (!mapping.IsValid) continue;
var mappingPath = Path.Combine(mappingsFolder, mapping.FileName);
if (!File.Exists(mappingPath))

View File

@ -324,12 +324,6 @@ public class SettingsViewModel : ViewModel
UserSettings.Default.OverridedOptions[_game] = SelectedOptions;
}
if (UserSettings.Default.CustomEndpoints.TryGetValue(_game, out var endpoints))
{
endpoints[0] = AesEndpoint;
endpoints[1] = MappingEndpoint;
}
UserSettings.Default.AssetLanguage = SelectedAssetLanguage;
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;
UserSettings.Default.CosmeticStyle = SelectedCosmeticStyle;

View File

@ -6,7 +6,7 @@
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" ResizeMode="NoResize" Closing="OnClosing"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}"
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.30'}">
<adonisControls:AdonisWindow.Resources>
@ -23,38 +23,62 @@
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Margin="{adonisUi:Space 1, 0.5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Text="Endpoint" VerticalAlignment="Center" Margin="0 0 0 5" />
<TextBox x:Name="EndpointUrl" Grid.Column="2" Margin="0 0 0 5" />
<Button Grid.Column="4" Content="Send" HorizontalAlignment="Right" Margin="0 0 0 5"
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}" Click="OnSend"/>
<Grid Grid.Row="0" Margin="{adonisUi:Space 1, 0.5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Endpoint" VerticalAlignment="Center" Margin="0 0 0 5" />
<TextBox Grid.Column="2" Margin="0 0 0 5" Text="{Binding Url, Mode=TwoWay}" TextChanged="OnTextChanged" />
<Button Grid.Column="4" Content="Send" HorizontalAlignment="Right" Margin="0 0 0 5"
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}" Click="OnSend"/>
</Grid>
<avalonEdit:TextEditor x:Name="EndpointResponse" Grid.Row="2" Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
FontFamily="Consolas" FontSize="8pt" IsReadOnly="True" ShowLineNumbers="True" Foreground="#DAE5F2" />
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<avalonEdit:TextEditor x:Name="EndpointResponse" Grid.Column="0" Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
<Grid Grid.Row="0" Margin="{adonisUi:Space 1, 0.5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Path" VerticalAlignment="Center" Margin="0 0 0 5" />
<TextBox Grid.Column="2" Margin="0 0 0 5" Text="{Binding Path, Mode=TwoWay}" TextChanged="OnTextChanged" />
<Button Grid.Column="4" Content="Test" HorizontalAlignment="Right" Margin="0 0 0 5"
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}" Click="OnTest"/>
</Grid>
<avalonEdit:TextEditor x:Name="TargetResponse" Grid.Row="2" Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
FontFamily="Consolas" FontSize="8pt" IsReadOnly="True" ShowLineNumbers="True" Foreground="#DAE5F2" />
<avalonEdit:TextEditor x:Name="TargetResponse" Grid.Column="2" Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}"
FontFamily="Consolas" FontSize="8pt" IsReadOnly="False" ShowLineNumbers="True" Foreground="#DAE5F2" />
</Grid>
</Grid>
@ -65,12 +89,17 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" MinWidth="78" Margin="0 0 12 0" IsDefault="True" IsCancel="False"
<TextBlock Grid.Column="0" Text="{Binding Label}"
HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="11" Margin="0 0 10 0" FontWeight="DemiBold"
Foreground="{DynamicResource {x:Static adonisUi:Brushes.Layer1InteractionForegroundBrush}}" />
<Button Grid.Column="1" MinWidth="78" Margin="0 0 12 0" IsDefault="True" IsCancel="False"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="OK" Click="OnClick" />
<Button Grid.Column="1" MinWidth="78" Margin="0 0 12 0" IsDefault="False" IsCancel="True"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Cancel" />
<Button Grid.Column="2" MinWidth="78" Margin="0 0 12 0" IsDefault="False" IsCancel="False"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Online Evaluator" Click="OnEvaluator" />
</Grid>
</Border>
</Grid>

View File

@ -1,8 +1,10 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using FModel.Extensions;
using FModel.Framework;
using FModel.ViewModels.ApiEndpoints.Models;
using FModel.Services;
using ICSharpCode.AvalonEdit.Document;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -12,61 +14,71 @@ namespace FModel.Views.Resources.Controls;
public partial class EndpointEditor
{
private readonly AesResponse _defaultAesResponse = new ();
private readonly MappingsResponse[] _defaultMappingResponse = {new ()};
private JObject _body;
public FEndpoint Endpoint { get; private set; }
private readonly EEndpointType _type;
private bool _isTested;
public EndpointEditor(FEndpoint endpoint, string title, EEndpointType type)
{
DataContext = endpoint;
_type = type;
_isTested = endpoint.IsValid;
InitializeComponent();
Title = title;
EndpointUrl.Text = endpoint.Url;
TargetResponse.SyntaxHighlighting = EndpointResponse.SyntaxHighlighting = AvalonExtensions.HighlighterSelector("json");
TargetResponse.Document = new TextDocument
{
Text = JsonConvert.SerializeObject(type switch
{
EEndpointType.Aes => _defaultAesResponse,
EEndpointType.Mapping => _defaultMappingResponse,
_ => throw new NotImplementedException()
}, Formatting.Indented)
};
TargetResponse.SyntaxHighlighting =
EndpointResponse.SyntaxHighlighting = AvalonExtensions.HighlighterSelector("json");
}
private void OnClick(object sender, RoutedEventArgs e)
{
Endpoint = new FEndpoint(EndpointUrl.Text);
DialogResult = true;
Close();
DialogResult = DataContext is FEndpoint { IsValid: true };
}
private async void OnSend(object sender, RoutedEventArgs e)
{
try
{
var response = await new RestClient().ExecuteAsync(new FRestRequest(EndpointUrl.Text)
{
OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; }
}).ConfigureAwait(false);
_body = JObject.Parse(response.Content!);
if (DataContext is not FEndpoint endpoint) return;
Application.Current.Dispatcher.Invoke(delegate
{
EndpointResponse.Document = new TextDocument
{
Text = _body.ToString(Formatting.Indented)
};
});
}
catch
var response = await new RestClient().ExecuteAsync(new FRestRequest(endpoint.Url)
{
//
}
OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; }
}).ConfigureAwait(false);
var body = JToken.Parse(response.Content!);
Application.Current.Dispatcher.Invoke(delegate
{
EndpointResponse.Document ??= new TextDocument();
EndpointResponse.Document.Text = body.ToString(Formatting.Indented);
});
}
private void OnTest(object sender, RoutedEventArgs e)
{
if (DataContext is not FEndpoint endpoint) return;
endpoint.TryValidate(ApplicationService.ApiEndpointView.DynamicApi, _type, out var response);
_isTested = true;
TargetResponse.Document ??= new TextDocument();
TargetResponse.Document.Text = JsonConvert.SerializeObject(response, Formatting.Indented);
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (sender is not TextBox { IsLoaded: true } ||
DataContext is not FEndpoint endpoint) return;
endpoint.IsValid = false;
}
private void OnEvaluator(object sender, RoutedEventArgs e)
{
Process.Start(new ProcessStartInfo { FileName = "https://jsonpath.herokuapp.com/", UseShellExecute = true });
}
private void OnClosing(object sender, CancelEventArgs e)
{
if (!_isTested) OnTest(null, null);
}
}

View File

@ -17,10 +17,10 @@ public class EndpointToTypeConverter : IMultiValueConverter
values[1] is not EEndpointType type)
return false;
var isEnabled = UserSettings.IsEndpointEnabled(viewModel.CUE4Parse.Game, type, out _);
var isValid = UserSettings.IsEndpointValid(viewModel.CUE4Parse.Game, type, out _);
return targetType switch
{
not null when targetType == typeof(Visibility) => isEnabled ? Visibility.Visible : Visibility.Collapsed,
not null when targetType == typeof(Visibility) => isValid ? Visibility.Visible : Visibility.Collapsed,
_ => throw new NotImplementedException()
};
}

View File

@ -201,11 +201,11 @@
<TextBlock Grid.Row="13" Grid.Column="0" Text="AES Reload at Launch" VerticalAlignment="Center" Margin="0 0 0 5"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
Visibility="{Binding SettingsView.AesEndpoint.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}" />
Visibility="{Binding SettingsView.AesEndpoint.IsValid, Converter={StaticResource BoolToVisibilityConverter}}" />
<ComboBox Grid.Row="13" Grid.Column="2" Grid.ColumnSpan="5" Margin="0 0 0 5"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
ItemsSource="{Binding SettingsView.AesReloads}" SelectedItem="{Binding SettingsView.SelectedAesReload, Mode=TwoWay}"
Visibility="{Binding SettingsView.AesEndpoint.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}">
Visibility="{Binding SettingsView.AesEndpoint.IsValid, Converter={StaticResource BoolToVisibilityConverter}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
@ -219,18 +219,17 @@
<TextBlock Grid.Row="15" Grid.Column="0" Text="Overwrite Mapping File" VerticalAlignment="Center" Margin="0 0 0 5"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
Visibility="{Binding SettingsView.MappingEndpoint.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}" />
Visibility="{Binding SettingsView.MappingEndpoint.IsValid, Converter={StaticResource BoolToVisibilityConverter}}" />
<CheckBox Grid.Row="15" Grid.Column="2" Margin="0 5 0 10"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
Visibility="{Binding SettingsView.MappingEndpoint.IsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"
Visibility="{Binding SettingsView.MappingEndpoint.IsValid, Converter={StaticResource BoolToVisibilityConverter}}"
IsChecked="{Binding SettingsView.MappingEndpoint.Overwrite, Mode=TwoWay}" />
<TextBlock Grid.Row="16" Grid.Column="0" Text="Mapping File Path" VerticalAlignment="Center" Margin="0 0 0 5"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
Visibility="{Binding SettingsView.MappingEndpoint.Overwrite, Converter={StaticResource BoolToVisibilityConverter}}">
</TextBlock>
<TextBox Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 0 0 5" Text="{Binding SettingsView.MappingEndpoint.Path, Mode=TwoWay}"
Visibility="{Binding SettingsView.MappingEndpoint.Overwrite, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBox Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 0 0 5" Text="{Binding SettingsView.MappingEndpoint.FilePath, Mode=TwoWay}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
Visibility="{Binding SettingsView.MappingEndpoint.Overwrite, Converter={StaticResource BoolToVisibilityConverter}}" />
<Button Grid.Row="16" Grid.Column="6" Content="..." HorizontalAlignment="Right" Click="OnBrowseMappings" Margin="0 0 0 5"

View File

@ -102,7 +102,7 @@ public partial class SettingsView
if (!openFileDialog.ShowDialog().GetValueOrDefault())
return;
_applicationView.SettingsView.MappingEndpoint.Path = openFileDialog.FileName;
_applicationView.SettingsView.MappingEndpoint.FilePath = openFileDialog.FileName;
await _applicationView.CUE4Parse.InitBenMappings();
}
@ -173,26 +173,14 @@ public partial class SettingsView
private void OpenAesEndpoint(object sender, RoutedEventArgs e)
{
var editor = new EndpointEditor(
_applicationView.SettingsView.AesEndpoint,
"Endpoint Configuration (AES)",
EEndpointType.Aes);
var result = editor.ShowDialog();
if (!result.HasValue || !result.Value)
return;
_applicationView.SettingsView.AesEndpoint = editor.Endpoint;
_applicationView.SettingsView.AesEndpoint, "Endpoint Configuration (AES)", EEndpointType.Aes);
editor.ShowDialog();
}
private void OpenMappingEndpoint(object sender, RoutedEventArgs e)
{
var editor = new EndpointEditor(
_applicationView.SettingsView.MappingEndpoint,
"Endpoint Configuration (Mapping)",
EEndpointType.Mapping);
var result = editor.ShowDialog();
if (!result.HasValue || !result.Value)
return;
_applicationView.SettingsView.MappingEndpoint = editor.Endpoint;
_applicationView.SettingsView.MappingEndpoint, "Endpoint Configuration (Mapping)", EEndpointType.Mapping);
editor.ShowDialog();
}
}