mirror of
https://github.com/4sval/FModel.git
synced 2026-03-25 03:04:53 -05:00
commit
bc2285adf0
164
.editorconfig
Normal file
164
.editorconfig
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
indent_size = 4
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_within_query_expression_clauses = true
|
||||
|
||||
# Code files
|
||||
[*.{cs,csx,vb,vbx}]
|
||||
indent_size = 4
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
|
||||
# avoid this. unless absolutely necessary
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
|
||||
# only use var when it's obvious what the variable type is
|
||||
csharp_style_var_for_built_in_types = false:none
|
||||
csharp_style_var_when_type_is_apparent = false:none
|
||||
csharp_style_var_elsewhere = false:suggestion
|
||||
|
||||
# use language keywords instead of BCL types
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# name all constant fields using PascalCase
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# internal and private fields should be _camelCase
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
|
||||
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
|
||||
|
||||
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
|
||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
||||
|
||||
# Code style defaults
|
||||
dotnet_sort_system_directives_first = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = false
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = false:none
|
||||
csharp_style_expression_bodied_constructors = false:none
|
||||
csharp_style_expression_bodied_operators = false:none
|
||||
csharp_style_expression_bodied_properties = true:none
|
||||
csharp_style_expression_bodied_indexers = true:none
|
||||
csharp_style_expression_bodied_accessors = true:none
|
||||
|
||||
# Pattern matching
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
|
||||
# Null checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = do_not_ignore
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
[*.{asm,inc}]
|
||||
indent_size = 8
|
||||
|
||||
# Visual Studio Solution Files
|
||||
[*.sln]
|
||||
indent_style = tab
|
||||
|
||||
# Visual Studio XML Project Files
|
||||
[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
indent_size = 2
|
||||
|
||||
# XML Configuration Files
|
||||
[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
|
||||
indent_size = 2
|
||||
|
||||
[CMakeLists.txt]
|
||||
indent_size = 2
|
||||
|
||||
# Makefiles
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Batch Files
|
||||
[*.{cmd,bat}]
|
||||
indent_size = 2
|
||||
end_of_line = crlf
|
||||
|
||||
# Bash Files
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
|
||||
# Web Files
|
||||
[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,pcss,svg,vue}]
|
||||
indent_size = 2
|
||||
|
||||
# Markdown Files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# JSON Files
|
||||
[*.{json,json5,webmanifest}]
|
||||
indent_size = 2
|
||||
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
|
|
@ -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 net5.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
|
||||
|
|
@ -45,4 +45,4 @@ jobs:
|
|||
automatic_release_tag: ${{ github.event.inputs.appVersion }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
prerelease: false
|
||||
files: FModel.zip
|
||||
files: FModel.zip
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 6d911668e730b3f4c1eab0b3f7e9a99bc3dde943
|
||||
Subproject commit d2163de354e97a0e62222221b96666a678255884
|
||||
|
|
@ -25,7 +25,7 @@ namespace FModel
|
|||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool AttachConsole(int dwProcessId);
|
||||
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
#if DEBUG
|
||||
|
|
@ -98,7 +98,7 @@ namespace FModel
|
|||
{
|
||||
if ((EErrorKind) messageBox.ButtonPressed.Id == EErrorKind.ResetSettings)
|
||||
UserSettings.Default = new UserSettings();
|
||||
|
||||
|
||||
ApplicationService.ApplicationView.Restart();
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ namespace FModel
|
|||
return $"{productName} ({(Environment.Is64BitOperatingSystem ? "64" : "32")}-bit)";
|
||||
}
|
||||
|
||||
private string GetRegistryValue(string path, string name = null, RegistryHive root = RegistryHive.CurrentUser)
|
||||
public static string GetRegistryValue(string path, string name = null, RegistryHive root = RegistryHive.CurrentUser)
|
||||
{
|
||||
using var rk = RegistryKey.OpenBaseKey(root, RegistryView.Default).OpenSubKey(path);
|
||||
if (rk != null)
|
||||
|
|
@ -131,4 +131,4 @@ namespace FModel
|
|||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ namespace FModel.Creator.Bases.FN
|
|||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset == EEnabledDisabled.Enabled);
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
|
||||
|
||||
if (Object.TryGetValue(out FPackageIndex series, "Series") && Utils.TryGetPackageIndexExport(series, out UObject export))
|
||||
_rarityName = export.Name;
|
||||
else
|
||||
_rarityName = GetRarityName(Object.GetOrDefault<FName>("Rarity"));
|
||||
_rarityName = Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription();
|
||||
|
||||
if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
|
||||
CheckGameplayTags(gameplayTags);
|
||||
|
|
@ -108,44 +108,6 @@ namespace FModel.Creator.Bases.FN
|
|||
return number > 10 ? $"C{number / 10 + 1} S{s[^1..]}" : $"C1 S{s}";
|
||||
}
|
||||
|
||||
private string GetRarityName(FName r)
|
||||
{
|
||||
var rarity = EFortRarity.Uncommon;
|
||||
switch (r.Text)
|
||||
{
|
||||
case "EFortRarity::Common":
|
||||
case "EFortRarity::Handmade":
|
||||
rarity = EFortRarity.Common;
|
||||
break;
|
||||
case "EFortRarity::Rare":
|
||||
case "EFortRarity::Sturdy":
|
||||
rarity = EFortRarity.Rare;
|
||||
break;
|
||||
case "EFortRarity::Epic":
|
||||
case "EFortRarity::Quality":
|
||||
rarity = EFortRarity.Epic;
|
||||
break;
|
||||
case "EFortRarity::Legendary":
|
||||
case "EFortRarity::Fine":
|
||||
rarity = EFortRarity.Legendary;
|
||||
break;
|
||||
case "EFortRarity::Mythic":
|
||||
case "EFortRarity::Elegant":
|
||||
rarity = EFortRarity.Mythic;
|
||||
break;
|
||||
case "EFortRarity::Transcendent":
|
||||
case "EFortRarity::Masterwork":
|
||||
rarity = EFortRarity.Transcendent;
|
||||
break;
|
||||
case "EFortRarity::Unattainable":
|
||||
case "EFortRarity::Badass":
|
||||
rarity = EFortRarity.Unattainable;
|
||||
break;
|
||||
}
|
||||
|
||||
return rarity.GetDescription();
|
||||
}
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
|
||||
|
|
@ -302,4 +264,4 @@ namespace FModel.Creator.Bases.FN
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ namespace FModel.Creator.Bases.FN
|
|||
{
|
||||
// rarity
|
||||
if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series);
|
||||
else GetRarity(Object.GetOrDefault<FName>("Rarity")); // default is uncommon
|
||||
else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon
|
||||
|
||||
// preview
|
||||
if (isUsingDisplayAsset && Utils.TryGetDisplayAsset(Object, out var preview))
|
||||
Preview = preview;
|
||||
else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition"))
|
||||
Preview = Utils.GetBitmap(itemDefinition);
|
||||
else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "SidePanelIcon", "EntryListIcon", "SmallPreviewImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon"))
|
||||
else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon"))
|
||||
Preview = Utils.GetBitmap(largePreview);
|
||||
else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s))
|
||||
Preview = Utils.GetBitmap(s);
|
||||
|
|
@ -78,7 +78,7 @@ namespace FModel.Creator.Bases.FN
|
|||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset == EEnabledDisabled.Enabled);
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
|
||||
|
||||
if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
|
||||
CheckGameplayTags(gameplayTags);
|
||||
|
|
@ -165,44 +165,11 @@ namespace FModel.Creator.Bases.FN
|
|||
}
|
||||
}
|
||||
|
||||
private void GetRarity(FName r)
|
||||
private void GetRarity(EFortRarity r)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return;
|
||||
|
||||
var rarity = EFortRarity.Uncommon;
|
||||
switch (r.Text)
|
||||
{
|
||||
case "EFortRarity::Common":
|
||||
case "EFortRarity::Handmade":
|
||||
rarity = EFortRarity.Common;
|
||||
break;
|
||||
case "EFortRarity::Rare":
|
||||
case "EFortRarity::Sturdy":
|
||||
rarity = EFortRarity.Rare;
|
||||
break;
|
||||
case "EFortRarity::Epic":
|
||||
case "EFortRarity::Quality":
|
||||
rarity = EFortRarity.Epic;
|
||||
break;
|
||||
case "EFortRarity::Legendary":
|
||||
case "EFortRarity::Fine":
|
||||
rarity = EFortRarity.Legendary;
|
||||
break;
|
||||
case "EFortRarity::Mythic":
|
||||
case "EFortRarity::Elegant":
|
||||
rarity = EFortRarity.Mythic;
|
||||
break;
|
||||
case "EFortRarity::Transcendent":
|
||||
case "EFortRarity::Masterwork":
|
||||
rarity = EFortRarity.Transcendent;
|
||||
break;
|
||||
case "EFortRarity::Unattainable":
|
||||
case "EFortRarity::Badass":
|
||||
rarity = EFortRarity.Unattainable;
|
||||
break;
|
||||
}
|
||||
|
||||
if (export.GetByIndex<FStructFallback>((int) rarity) is { } data &&
|
||||
if (export.GetByIndex<FStructFallback>((int) r) is { } data &&
|
||||
data.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
data.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
data.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
|
|
@ -308,4 +275,4 @@ namespace FModel.Creator.Bases.FN
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ namespace FModel.Creator.Bases.FN
|
|||
weaponRowValue.TryGetValue(out UDataTable durabilityTable, "Durability") &&
|
||||
weaponRowValue.TryGetValue(out FName durabilityRowName, "DurabilityRowName") &&
|
||||
durabilityTable.TryGetDataTableRow(durabilityRowName.Text, StringComparison.OrdinalIgnoreCase, out var durability) &&
|
||||
durability.TryGetValue(out int duraByRarity, GetRarityName(Object.GetOrDefault<FName>("Rarity"))))
|
||||
durability.TryGetValue(out int duraByRarity, Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription()))
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6FA2882140CB69DE32FD73A392F0585B", "Durability"), duraByRarity, 20));
|
||||
}
|
||||
|
|
@ -145,7 +145,7 @@ namespace FModel.Creator.Bases.FN
|
|||
curveTable.TryGetCurveTableRow(rowName.Text, StringComparison.OrdinalIgnoreCase, out var rowValue) &&
|
||||
rowValue.TryGetValue(out FSimpleCurveKey[] keys, "Keys") && keys.Length > 0)
|
||||
{
|
||||
statValue = keys[0].KeyValue;
|
||||
statValue = keys[0].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -153,44 +153,6 @@ namespace FModel.Creator.Bases.FN
|
|||
return false;
|
||||
}
|
||||
|
||||
private string GetRarityName(FName r)
|
||||
{
|
||||
var rarity = EFortRarity.Uncommon;
|
||||
switch (r.Text)
|
||||
{
|
||||
case "EFortRarity::Common":
|
||||
case "EFortRarity::Handmade":
|
||||
rarity = EFortRarity.Common;
|
||||
break;
|
||||
case "EFortRarity::Rare":
|
||||
case "EFortRarity::Sturdy":
|
||||
rarity = EFortRarity.Rare;
|
||||
break;
|
||||
case "EFortRarity::Epic":
|
||||
case "EFortRarity::Quality":
|
||||
rarity = EFortRarity.Epic;
|
||||
break;
|
||||
case "EFortRarity::Legendary":
|
||||
case "EFortRarity::Fine":
|
||||
rarity = EFortRarity.Legendary;
|
||||
break;
|
||||
case "EFortRarity::Mythic":
|
||||
case "EFortRarity::Elegant":
|
||||
rarity = EFortRarity.Mythic;
|
||||
break;
|
||||
case "EFortRarity::Transcendent":
|
||||
case "EFortRarity::Masterwork":
|
||||
rarity = EFortRarity.Transcendent;
|
||||
break;
|
||||
case "EFortRarity::Unattainable":
|
||||
case "EFortRarity::Badass":
|
||||
rarity = EFortRarity.Unattainable;
|
||||
break;
|
||||
}
|
||||
|
||||
return rarity.GetDescription();
|
||||
}
|
||||
|
||||
private readonly SKPaint _informationPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
|
|
@ -293,7 +255,7 @@ namespace FModel.Creator.Bases.FN
|
|||
|
||||
public void Draw(SKCanvas c, SKColor sliderColor, int width, int height, ref float y)
|
||||
{
|
||||
while (_statPaint.MeasureText(_statName) > height * 2)
|
||||
while (_statPaint.MeasureText(_statName) > height * 2 - 40)
|
||||
{
|
||||
_statPaint.TextSize -= 1;
|
||||
}
|
||||
|
|
@ -316,4 +278,4 @@ namespace FModel.Creator.Bases.FN
|
|||
c.DrawRect(new SKRect(height * 2, y, Math.Min(height * 2 + sliderWidth, sliderRight), y + 5), _statPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
|
|
@ -72,9 +74,15 @@ namespace FModel.Creator.Bases.FN
|
|||
Description += "\n" + completionText.Text;
|
||||
if (Object.TryGetValue(out FSoftObjectPath tandemCharacterData, "TandemCharacterData") &&
|
||||
Utils.TryLoadObject(tandemCharacterData.AssetPathName.Text, out UObject uObject) &&
|
||||
uObject.TryGetValue(out FSoftObjectPath tandemIcon, "SidePanelIcon", "EntryListIcon", "ToastIcon"))
|
||||
uObject.TryGetValue(out FSoftObjectPath tandemIcon, "EntryListIcon", "ToastIcon") &&
|
||||
Utils.TryLoadObject(tandemIcon.AssetPathName.Text, out UObject iconObject))
|
||||
{
|
||||
Preview = Utils.GetBitmap(tandemIcon);
|
||||
Preview = iconObject switch
|
||||
{
|
||||
UTexture2D text => Utils.GetBitmap(text),
|
||||
UMaterialInstanceConstant mat => Utils.GetBitmap(mat),
|
||||
_ => Preview
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
|
@ -46,7 +46,7 @@ namespace FModel.Creator
|
|||
var path = $"/Game/Catalog/MI_OfferImages/MI_{uObject.Name.Replace("Athena_Commando_", string.Empty)}";
|
||||
if (!TryLoadObject(path, out UMaterialInstanceConstant material)) // non-obfuscated item definition
|
||||
{
|
||||
if (!TryLoadObject($"{path[..path.LastIndexOf('_')]}_{path.SubstringAfterLast('_').ToLower()}", out material)) // Try to get MI with lowercase obfuscation
|
||||
if (!TryLoadObject($"{path[..path.LastIndexOf('_')]}_{path.SubstringAfterLast('_').ToLower()}", out material)) // Try to get MI with lowercase obfuscation
|
||||
TryLoadObject(path[..path.LastIndexOf('_')], out material); // hopefully gets obfuscated item definition
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +93,7 @@ namespace FModel.Creator
|
|||
case "TextureB":
|
||||
case "OfferImage":
|
||||
case "KeyArtTexture":
|
||||
case "NPC-Portrait":
|
||||
{
|
||||
return GetBitmap(texture);
|
||||
}
|
||||
|
|
@ -122,11 +123,11 @@ namespace FModel.Creator
|
|||
me.ScalePixels(pixmap, SKFilterQuality.Medium);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public static void ClearToTransparent(this SKBitmap me) {
|
||||
|
||||
public static void ClearToTransparent(this SKBitmap me, SKColor colorToDelete) {
|
||||
var colors = me.Pixels;
|
||||
for (var n = 0; n < colors.Length; n++) {
|
||||
if (colors[n] != SKColors.Black) continue;
|
||||
if (colors[n] != colorToDelete) continue;
|
||||
colors[n] = SKColors.Transparent;
|
||||
}
|
||||
me.Pixels = colors;
|
||||
|
|
@ -274,4 +275,4 @@ namespace FModel.Creator
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,14 +51,6 @@ namespace FModel
|
|||
Never
|
||||
}
|
||||
|
||||
public enum EEnabledDisabled
|
||||
{
|
||||
[Description("Disabled")]
|
||||
Disabled,
|
||||
[Description("Enabled")]
|
||||
Enabled
|
||||
}
|
||||
|
||||
public enum FGame
|
||||
{
|
||||
[Description("Unknown")]
|
||||
|
|
@ -90,7 +82,13 @@ namespace FModel
|
|||
[Description("Core")]
|
||||
Platform,
|
||||
[Description("Days Gone")]
|
||||
BendGame
|
||||
BendGame,
|
||||
[Description("PLAYERUNKNOWN'S BATTLEGROUNDS")]
|
||||
TslGame,
|
||||
[Description("Splitgate")]
|
||||
PortalWars,
|
||||
[Description("GTA: The Trilogy - Definitive Edition")]
|
||||
Gameface
|
||||
}
|
||||
|
||||
public enum ELoadingMode
|
||||
|
|
@ -138,4 +136,4 @@ namespace FModel
|
|||
// [Description("Community")]
|
||||
// CommunityMade
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>FModel.ico</ApplicationIcon>
|
||||
<Version>4.0.0</Version>
|
||||
<AssemblyVersion>4.0.1.1</AssemblyVersion>
|
||||
<FileVersion>4.0.1.1</FileVersion>
|
||||
<AssemblyVersion>4.0.2.0</AssemblyVersion>
|
||||
<FileVersion>4.0.2.0</FileVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsPublishable>true</IsPublishable>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
|
@ -102,20 +102,20 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AdonisUI.ClassicTheme.NET5" Version="1.17.1" />
|
||||
<PackageReference Include="AdonisUI.NET5" Version="1.17.1" />
|
||||
<PackageReference Include="AdonisUI" Version="1.17.1" />
|
||||
<PackageReference Include="AdonisUI.ClassicTheme" Version="1.17.1" />
|
||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.0" />
|
||||
<PackageReference Include="AvalonEdit" Version="6.1.2.30" />
|
||||
<PackageReference Include="CSCore" Version="1.2.1.2" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="EpicManifestParser" Version="1.2.69-temp" />
|
||||
<PackageReference Include="HelixToolkit.SharpDX.Core.Wpf" Version="2.17.0" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.12" />
|
||||
<PackageReference Include="EpicManifestParser" Version="1.2.70-temp" />
|
||||
<PackageReference Include="HelixToolkit.SharpDX.Core.Wpf" Version="2.20.0" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.2.15" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.3" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.4" />
|
||||
<PackageReference Include="Oodle.NET" Version="1.0.1" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="3.1.0" />
|
||||
<PackageReference Include="RestSharp" Version="106.12.0" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.0" />
|
||||
<PackageReference Include="RestSharp" Version="106.13.0" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.3" />
|
||||
|
|
@ -186,6 +186,7 @@
|
|||
<Resource Include="Resources\delete.png" />
|
||||
<Resource Include="Resources\edit.png" />
|
||||
<Resource Include="Resources\go_to_directory.png" />
|
||||
<Resource Include="Resources\approaching_storm_cubemap.dds" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30804.86
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31912.275
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FModel", "FModel.csproj", "{B1F494EA-90A6-4C24-800E-2F724A1884CA}"
|
||||
EndProject
|
||||
|
|
|
|||
34
FModel/Framework/NavigationList.cs
Normal file
34
FModel/Framework/NavigationList.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Framework
|
||||
{
|
||||
public class NavigationList<T> : List<T>
|
||||
{
|
||||
private int _currentIndex = 0;
|
||||
public int CurrentIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentIndex > Count - 1) { _currentIndex = Count - 1; }
|
||||
if (_currentIndex < 0) { _currentIndex = 0; }
|
||||
return _currentIndex;
|
||||
}
|
||||
set { _currentIndex = value; }
|
||||
}
|
||||
|
||||
public T MoveNext
|
||||
{
|
||||
get { _currentIndex++; return this[CurrentIndex]; }
|
||||
}
|
||||
|
||||
public T MovePrevious
|
||||
{
|
||||
get { _currentIndex--; return this[CurrentIndex]; }
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get { return this[CurrentIndex]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,4 +85,4 @@ namespace FModel
|
|||
return -d < n && d > n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,15 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Archives Info" Command="{Binding MenuCommand}" CommandParameter="Directory_ArchivesInfo" IsEnabled="{Binding IsReady}">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource InfoIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Assets">
|
||||
<MenuItem Header="Search" IsEnabled="{Binding IsReady}" InputGestureText="Ctrl+Shift+F" Click="OnSearchViewClick">
|
||||
|
|
@ -147,18 +156,15 @@
|
|||
<MenuItem Header="Save Textures" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Save Materials" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Save Meshes" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Save Animations" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
<MenuItem Header="Open Meshes & Materials" IsCheckable="True" StaysOpenOnClick="True"
|
||||
InputGestureText="{Binding AutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}}"
|
||||
IsChecked="{Binding IsAutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}}" />
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Views">
|
||||
|
|
@ -333,14 +339,14 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Image Source="/FModel;component/Resources/label.png"
|
||||
Width="16" Height="16" HorizontalAlignment="Center" Margin="0 0 3.5 0" />
|
||||
|
|
@ -448,7 +454,7 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding SelectedItem.AssetsList.Assets.Count, ElementName=AssetsFolderName, FallbackValue=0}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="Assets Count" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding SelectedItem.FoldersView.Count, ElementName=AssetsFolderName, FallbackValue=0}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
|
|
@ -472,7 +478,7 @@
|
|||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Grid Grid.Column="0" ZIndex="1" HorizontalAlignment="Left" Margin="5 2 0 0">
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -500,11 +506,11 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<controls:Breadcrumb Grid.Row="0" MaxWidth="{Binding ActualWidth, ElementName=AssetsSearchName}" HorizontalAlignment="Left" Margin="0 5 0 5"
|
||||
DataContext="{Binding SelectedItem.PathAtThisPoint, ElementName=AssetsFolderName, FallbackValue='No/Directory/Detected/In/Folder'}"/>
|
||||
|
||||
<ListBox Grid.Row="1" x:Name="AssetsListName" Style="{StaticResource AssetsListBox}" PreviewMouseDoubleClick="OnAssetsListMouseDoubleClick">
|
||||
|
||||
<ListBox Grid.Row="1" x:Name="AssetsListName" Style="{StaticResource AssetsListBox}" PreviewMouseDoubleClick="OnAssetsListMouseDoubleClick" PreviewKeyDown="OnPreviewKeyDown">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Extract in New Tab" Command="{Binding DataContext.RightClickMenuCommand}">
|
||||
|
|
@ -634,7 +640,7 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding SelectedItem.Offset, ElementName=AssetsListName, FallbackValue=0, StringFormat='{}0x{0:X}'}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="Offset" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding SelectedItem.Size, ElementName=AssetsListName, FallbackValue=0, Converter={x:Static converters:SizeToStringConverter.Instance}}" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
|
|
@ -664,7 +670,7 @@
|
|||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<TabControl Grid.Row="0" x:Name="TabControlName" Style="{StaticResource GameFilesTabControl}" />
|
||||
<Expander Grid.Row="1" Margin="0 5 0 5" ExpandDirection="Down">
|
||||
<Grid>
|
||||
|
|
@ -673,7 +679,7 @@
|
|||
<ColumnDefinition Width="2" />
|
||||
<ColumnDefinition Width="24" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<controls:CustomRichTextBox Grid.Column="0" Grid.ColumnSpan="3" x:Name="LogRtbName" Style="{StaticResource CustomRichTextBox}" />
|
||||
<StackPanel Grid.Column="2" Orientation="Vertical" VerticalAlignment="Bottom" Margin="-5 -5 5 5">
|
||||
<Button Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarButton}}" ToolTip="Open Output Folder" Padding="0,4,0,4"
|
||||
|
|
@ -834,32 +840,6 @@
|
|||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="TEX" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Materials Enabled">
|
||||
<StatusBarItem.Style>
|
||||
<Style TargetType="StatusBarItem">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsAutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
|
||||
<Setter Property="Visibility" Value="Hidden" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MAT" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="30" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Meshes Enabled">
|
||||
<StatusBarItem.Style>
|
||||
<Style TargetType="StatusBarItem">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsAutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
|
||||
<Setter Property="Visibility" Value="Hidden" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MSH" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="35" HorizontalContentAlignment="Stretch" ToolTip="Auto Save Animations Enabled">
|
||||
<StatusBarItem.Style>
|
||||
|
|
@ -886,6 +866,19 @@
|
|||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="SND" />
|
||||
</StatusBarItem>
|
||||
|
||||
<StatusBarItem Width="35" HorizontalContentAlignment="Stretch" ToolTip="Auto Open Meshes & Materials Enabled">
|
||||
<StatusBarItem.Style>
|
||||
<Style TargetType="StatusBarItem">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsAutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}}" Value="False">
|
||||
<Setter Property="Visibility" Value="Hidden" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StatusBarItem.Style>
|
||||
<TextBlock HorizontalAlignment="Center" FontWeight="SemiBold" Text="MSH" />
|
||||
</StatusBarItem>
|
||||
</StackPanel>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
|
|
|
|||
|
|
@ -33,14 +33,12 @@ namespace FModel
|
|||
{new KeyGesture(UserSettings.Default.AutoSaveProps.Key, UserSettings.Default.AutoSaveProps.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveTextures", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoSaveTextures.Key, UserSettings.Default.AutoSaveTextures.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveMaterials", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoSaveMaterials.Key, UserSettings.Default.AutoSaveMaterials.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveMeshes", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoSaveMeshes.Key, UserSettings.Default.AutoSaveMeshes.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoSaveAnimations", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoSaveAnimations.Key, UserSettings.Default.AutoSaveAnimations.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoOpenSounds", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoOpenSounds.Key, UserSettings.Default.AutoOpenSounds.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("AutoOpenMeshes", typeof(MainWindow), new InputGestureCollection
|
||||
{new KeyGesture(UserSettings.Default.AutoOpenMeshes.Key, UserSettings.Default.AutoOpenMeshes.Modifiers)}), OnAutoTriggerExecuted));
|
||||
CommandBindings.Add(new CommandBinding(new RoutedCommand("ReloadMappings", typeof(MainWindow), new InputGestureCollection {new KeyGesture(Key.F12)}), OnMappingsReload));
|
||||
CommandBindings.Add(new CommandBinding(ApplicationCommands.Find, (s, e) => OnOpenAvalonFinder()));
|
||||
|
||||
|
|
@ -148,18 +146,15 @@ namespace FModel
|
|||
case "AutoSaveTextures":
|
||||
UserSettings.Default.IsAutoSaveTextures = !UserSettings.Default.IsAutoSaveTextures;
|
||||
break;
|
||||
case "AutoSaveMaterials":
|
||||
UserSettings.Default.IsAutoSaveMaterials = !UserSettings.Default.IsAutoSaveMaterials;
|
||||
break;
|
||||
case "AutoSaveMeshes":
|
||||
UserSettings.Default.IsAutoSaveMeshes = !UserSettings.Default.IsAutoSaveMeshes;
|
||||
break;
|
||||
case "AutoSaveAnimations":
|
||||
UserSettings.Default.IsAutoSaveAnimations = !UserSettings.Default.IsAutoSaveAnimations;
|
||||
break;
|
||||
case "AutoOpenSounds":
|
||||
UserSettings.Default.IsAutoOpenSounds = !UserSettings.Default.IsAutoOpenSounds;
|
||||
break;
|
||||
case "AutoOpenMeshes":
|
||||
UserSettings.Default.IsAutoOpenMeshes = !UserSettings.Default.IsAutoOpenMeshes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,5 +243,18 @@ namespace FModel
|
|||
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;
|
||||
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Enter:
|
||||
var selectedItems = listBox.SelectedItems.Cast<AssetItem>().ToList();
|
||||
await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExtractSelected(cancellationToken, selectedItems); });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
FModel/Resources/approaching_storm_cubemap.dds
Normal file
BIN
FModel/Resources/approaching_storm_cubemap.dds
Normal file
Binary file not shown.
|
|
@ -36,7 +36,7 @@ namespace FModel.Settings
|
|||
{
|
||||
if (File.Exists(FilePath)) File.Delete(FilePath);
|
||||
}
|
||||
|
||||
|
||||
private bool _showChangelog = true;
|
||||
public bool ShowChangelog
|
||||
{
|
||||
|
|
@ -86,20 +86,6 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _isAutoSaveTextures, value);
|
||||
}
|
||||
|
||||
private bool _isAutoSaveMaterials;
|
||||
public bool IsAutoSaveMaterials
|
||||
{
|
||||
get => _isAutoSaveMaterials;
|
||||
set => SetProperty(ref _isAutoSaveMaterials, value);
|
||||
}
|
||||
|
||||
private bool _isAutoSaveMeshes;
|
||||
public bool IsAutoSaveMeshes
|
||||
{
|
||||
get => _isAutoSaveMeshes;
|
||||
set => SetProperty(ref _isAutoSaveMeshes, value);
|
||||
}
|
||||
|
||||
private bool _isAutoSaveAnimations;
|
||||
public bool IsAutoSaveAnimations
|
||||
{
|
||||
|
|
@ -114,6 +100,13 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _isAutoOpenSounds, value);
|
||||
}
|
||||
|
||||
private bool _isAutoOpenMeshes = true;
|
||||
public bool IsAutoOpenMeshes
|
||||
{
|
||||
get => _isAutoOpenMeshes;
|
||||
set => SetProperty(ref _isAutoOpenMeshes, value);
|
||||
}
|
||||
|
||||
private bool _isLoggerExpanded = true;
|
||||
public bool IsLoggerExpanded
|
||||
{
|
||||
|
|
@ -156,8 +149,8 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _updateMode, value);
|
||||
}
|
||||
|
||||
private EEnabledDisabled _keepDirectoryStructure = EEnabledDisabled.Enabled;
|
||||
public EEnabledDisabled KeepDirectoryStructure
|
||||
private bool _keepDirectoryStructure = true;
|
||||
public bool KeepDirectoryStructure
|
||||
{
|
||||
get => _keepDirectoryStructure;
|
||||
set => SetProperty(ref _keepDirectoryStructure, value);
|
||||
|
|
@ -198,8 +191,8 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _cosmeticStyle, value);
|
||||
}
|
||||
|
||||
private EEnabledDisabled _cosmeticDisplayAsset = EEnabledDisabled.Disabled;
|
||||
public EEnabledDisabled CosmeticDisplayAsset
|
||||
private bool _cosmeticDisplayAsset;
|
||||
public bool CosmeticDisplayAsset
|
||||
{
|
||||
get => _cosmeticDisplayAsset;
|
||||
set => SetProperty(ref _cosmeticDisplayAsset, value);
|
||||
|
|
@ -228,7 +221,10 @@ namespace FModel.Settings
|
|||
{FGame.RogueCompany, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.SwGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.Platform, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.BendGame, Constants._NO_PRESET_TRIGGER}
|
||||
{FGame.BendGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.TslGame, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.PortalWars, Constants._NO_PRESET_TRIGGER},
|
||||
{FGame.Gameface, Constants._NO_PRESET_TRIGGER}
|
||||
};
|
||||
public IDictionary<FGame, string> Presets
|
||||
{
|
||||
|
|
@ -252,7 +248,10 @@ namespace FModel.Settings
|
|||
{FGame.RogueCompany, EGame.GAME_RogueCompany},
|
||||
{FGame.SwGame, EGame.GAME_UE4_LATEST},
|
||||
{FGame.Platform, EGame.GAME_UE4_25},
|
||||
{FGame.BendGame, EGame.GAME_UE4_11}
|
||||
{FGame.BendGame, EGame.GAME_UE4_11},
|
||||
{FGame.TslGame, EGame.GAME_PlayerUnknownsBattlegrounds},
|
||||
{FGame.PortalWars, EGame.GAME_UE4_LATEST},
|
||||
{FGame.Gameface, EGame.GAME_GTATheTrilogyDefinitiveEdition}
|
||||
};
|
||||
public IDictionary<FGame, EGame> OverridedGame
|
||||
{
|
||||
|
|
@ -276,14 +275,17 @@ namespace FModel.Settings
|
|||
{FGame.RogueCompany, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.SwGame, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.Platform, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.BendGame, UE4Version.VER_UE4_DETERMINE_BY_GAME}
|
||||
{FGame.BendGame, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.TslGame, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.PortalWars, UE4Version.VER_UE4_DETERMINE_BY_GAME},
|
||||
{FGame.Gameface, UE4Version.VER_UE4_DETERMINE_BY_GAME}
|
||||
};
|
||||
public IDictionary<FGame, UE4Version> OverridedUEVersion
|
||||
{
|
||||
get => _overridedUEVersion;
|
||||
set => SetProperty(ref _overridedUEVersion, value);
|
||||
}
|
||||
|
||||
|
||||
private IDictionary<FGame, List<FCustomVersion>> _overridedCustomVersions = new Dictionary<FGame, List<FCustomVersion>>
|
||||
{
|
||||
{FGame.Unknown, null},
|
||||
|
|
@ -300,14 +302,17 @@ namespace FModel.Settings
|
|||
{FGame.RogueCompany, null},
|
||||
{FGame.SwGame, null},
|
||||
{FGame.Platform, null},
|
||||
{FGame.BendGame, null}
|
||||
{FGame.BendGame, null},
|
||||
{FGame.TslGame, null},
|
||||
{FGame.PortalWars, null},
|
||||
{FGame.Gameface, null}
|
||||
};
|
||||
public IDictionary<FGame, List<FCustomVersion>> OverridedCustomVersions
|
||||
{
|
||||
get => _overridedCustomVersions;
|
||||
set => SetProperty(ref _overridedCustomVersions, value);
|
||||
}
|
||||
|
||||
|
||||
private IDictionary<FGame, Dictionary<string, bool>> _overridedOptions = new Dictionary<FGame, Dictionary<string, bool>>
|
||||
{
|
||||
{FGame.Unknown, null},
|
||||
|
|
@ -324,7 +329,10 @@ namespace FModel.Settings
|
|||
{FGame.RogueCompany, null},
|
||||
{FGame.SwGame, null},
|
||||
{FGame.Platform, null},
|
||||
{FGame.BendGame, null}
|
||||
{FGame.BendGame, null},
|
||||
{FGame.TslGame, null},
|
||||
{FGame.PortalWars, null},
|
||||
{FGame.Gameface, null}
|
||||
};
|
||||
public IDictionary<FGame, Dictionary<string, bool>> OverridedOptions
|
||||
{
|
||||
|
|
@ -387,7 +395,10 @@ namespace FModel.Settings
|
|||
{FGame.RogueCompany, new List<CustomDirectory>()},
|
||||
{FGame.SwGame, new List<CustomDirectory>()},
|
||||
{FGame.Platform, new List<CustomDirectory>()},
|
||||
{FGame.BendGame, new List<CustomDirectory>()}
|
||||
{FGame.BendGame, new List<CustomDirectory>()},
|
||||
{FGame.TslGame, new List<CustomDirectory>()},
|
||||
{FGame.PortalWars, new List<CustomDirectory>()},
|
||||
{FGame.Gameface, new List<CustomDirectory>()}
|
||||
};
|
||||
public IDictionary<FGame, IList<CustomDirectory>> CustomDirectories
|
||||
{
|
||||
|
|
@ -472,34 +483,27 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _autoSaveTextures, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoSaveMaterials = new(Key.F4);
|
||||
public Hotkey AutoSaveMaterials
|
||||
{
|
||||
get => _autoSaveMaterials;
|
||||
set => SetProperty(ref _autoSaveMaterials, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoSaveMeshes = new(Key.F5);
|
||||
public Hotkey AutoSaveMeshes
|
||||
{
|
||||
get => _autoSaveMeshes;
|
||||
set => SetProperty(ref _autoSaveMeshes, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoSaveAnimations = new(Key.F6);
|
||||
private Hotkey _autoSaveAnimations = new(Key.F4);
|
||||
public Hotkey AutoSaveAnimations
|
||||
{
|
||||
get => _autoSaveAnimations;
|
||||
set => SetProperty(ref _autoSaveAnimations, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoOpenSounds = new(Key.F7);
|
||||
private Hotkey _autoOpenSounds = new(Key.F5);
|
||||
public Hotkey AutoOpenSounds
|
||||
{
|
||||
get => _autoOpenSounds;
|
||||
set => SetProperty(ref _autoOpenSounds, value);
|
||||
}
|
||||
|
||||
private Hotkey _autoOpenMeshes = new(Key.F6);
|
||||
public Hotkey AutoOpenMeshes
|
||||
{
|
||||
get => _autoOpenMeshes;
|
||||
set => SetProperty(ref _autoOpenMeshes, value);
|
||||
}
|
||||
|
||||
private Hotkey _addAudio = new(Key.N, ModifierKeys.Control);
|
||||
public Hotkey AddAudio
|
||||
{
|
||||
|
|
@ -534,14 +538,21 @@ namespace FModel.Settings
|
|||
get => _meshExportFormat;
|
||||
set => SetProperty(ref _meshExportFormat, value);
|
||||
}
|
||||
|
||||
|
||||
private ELodFormat _lodExportFormat = ELodFormat.FirstLod;
|
||||
public ELodFormat LodExportFormat
|
||||
{
|
||||
get => _lodExportFormat;
|
||||
set => SetProperty(ref _lodExportFormat, value);
|
||||
}
|
||||
|
||||
|
||||
private bool _saveSkeletonAsMesh;
|
||||
public bool SaveSkeletonAsMesh
|
||||
{
|
||||
get => _saveSkeletonAsMesh;
|
||||
set => SetProperty(ref _saveSkeletonAsMesh, value);
|
||||
}
|
||||
|
||||
private ETextureFormat _textureExportFormat = ETextureFormat.Png;
|
||||
public ETextureFormat TextureExportFormat
|
||||
{
|
||||
|
|
@ -549,4 +560,4 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _textureExportFormat, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
|
|
@ -45,6 +46,23 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
return GetMappingsAsync(token).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, Dictionary<string, string>>> GetHotfixesAsync(CancellationToken token, string language = "en-US")
|
||||
{
|
||||
var request = new RestRequest("https://benbot.app/api/v1/hotfixes", Method.GET)
|
||||
{
|
||||
OnBeforeDeserialization = resp => { resp.ContentType = "application/json; charset=utf-8"; }
|
||||
};
|
||||
request.AddParameter("lang", language);
|
||||
var response = await _client.ExecuteAsync<Dictionary<string, Dictionary<string, string>>>(request, token).ConfigureAwait(false);
|
||||
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int)response.StatusCode, request.Resource);
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
public Dictionary<string, Dictionary<string, string>> GetHotfixes(CancellationToken token, string language = "en-US")
|
||||
{
|
||||
return GetHotfixesAsync(token, language).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task DownloadFileAsync(string fileLink, string installationPath)
|
||||
{
|
||||
var request = new RestRequest(fileLink, Method.GET);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
private Game _game;
|
||||
private readonly IDictionary<string, CommunityDesign> _communityDesigns = new Dictionary<string, CommunityDesign>();
|
||||
private ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
|
||||
|
||||
public FModelApi(IRestClient client) : base(client)
|
||||
{
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
{
|
||||
return _backups ??= GetBackupsAsync(token, gameName).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
|
||||
public async Task<Game> GetGamesAsync(CancellationToken token, string gameName)
|
||||
{
|
||||
var request = new RestRequest($"https://api.fmodel.app/v1/games/{gameName}", Method.GET);
|
||||
|
|
@ -78,7 +78,7 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, request.Resource);
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
|
||||
public Game GetGames(CancellationToken token, string gameName)
|
||||
{
|
||||
return _game ??= GetGamesAsync(token, gameName).GetAwaiter().GetResult();
|
||||
|
|
@ -101,14 +101,14 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
_communityDesigns[designName] = communityDesign;
|
||||
return communityDesign;
|
||||
}
|
||||
|
||||
|
||||
public void CheckForUpdates(EUpdateMode updateMode)
|
||||
{
|
||||
AutoUpdater.ParseUpdateInfoEvent += ParseUpdateInfoEvent;
|
||||
AutoUpdater.CheckForUpdateEvent += CheckForUpdateEvent;
|
||||
AutoUpdater.Start($"https://api.fmodel.app/v1/infos/{updateMode}");
|
||||
}
|
||||
|
||||
|
||||
private void ParseUpdateInfoEvent(ParseUpdateInfoEventArgs args)
|
||||
{
|
||||
_infos = JsonConvert.DeserializeObject<Info>(args.RemoteData);
|
||||
|
|
@ -122,7 +122,7 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CheckForUpdateEvent(UpdateInfoEventArgs args)
|
||||
{
|
||||
if (args is {CurrentVersion: { }})
|
||||
|
|
@ -144,7 +144,7 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
Buttons = MessageBoxButtons.YesNo(),
|
||||
IsSoundEnabled = false
|
||||
};
|
||||
|
||||
|
||||
MessageBox.Show(messageBox);
|
||||
if (messageBox.Result != MessageBoxResult.Yes) return;
|
||||
|
||||
|
|
@ -175,11 +175,11 @@ namespace FModel.ViewModels.ApiEndpoints
|
|||
var request = new RestRequest(args.ChangelogURL, Method.GET);
|
||||
var response = _client.Execute(request);
|
||||
if (string.IsNullOrEmpty(response.Content)) return;
|
||||
|
||||
|
||||
_applicationView.CUE4Parse.TabControl.AddTab($"Release Notes: {args.CurrentVersion}");
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("changelog");
|
||||
_applicationView.CUE4Parse.TabControl.SelectedTab.SetDocumentText(response.Content, false);
|
||||
UserSettings.Default.ShowChangelog = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ namespace FModel.ViewModels
|
|||
AesManager = new AesManagerViewModel(CUE4Parse);
|
||||
MapViewer = new MapViewerViewModel(CUE4Parse);
|
||||
AudioPlayer = new AudioPlayerViewModel();
|
||||
ModelViewer = new ModelViewerViewModel();
|
||||
ModelViewer = new ModelViewerViewModel(CUE4Parse.Game);
|
||||
Status = EStatusKind.Ready;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -20,6 +20,7 @@ using CUE4Parse.UE4.Assets.Exports.Sound;
|
|||
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.IO;
|
||||
using CUE4Parse.UE4.Localization;
|
||||
using CUE4Parse.UE4.Oodle.Objects;
|
||||
using CUE4Parse.UE4.Shaders;
|
||||
|
|
@ -58,12 +59,20 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _game, value);
|
||||
}
|
||||
|
||||
private bool _modelIsSwappingMaterial;
|
||||
public bool ModelIsSwappingMaterial
|
||||
{
|
||||
get => _modelIsSwappingMaterial;
|
||||
set => SetProperty(ref _modelIsSwappingMaterial, value);
|
||||
}
|
||||
|
||||
public AbstractVfsFileProvider Provider { get; }
|
||||
public GameDirectoryViewModel GameDirectory { get; }
|
||||
public AssetsFolderViewModel AssetsFolder { get; }
|
||||
public SearchViewModel SearchVm { get; }
|
||||
public TabControlViewModel TabControl { get; }
|
||||
public int LocalizedResourcesCount { get; set; }
|
||||
public bool HotfixedResourcesDone { get; set; } = false;
|
||||
public int VirtualPathCount { get; set; }
|
||||
|
||||
public CUE4ParseViewModel(string gameDirectory)
|
||||
|
|
@ -138,7 +147,7 @@ namespace FModel.ViewModels
|
|||
|
||||
byte[] manifestData;
|
||||
var chunksDir = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"));
|
||||
var manifestPath = Path.Combine(chunksDir.FullName, manifestInfo.Filename);
|
||||
var manifestPath = Path.Combine(chunksDir.FullName, manifestInfo.FileName);
|
||||
if (File.Exists(manifestPath))
|
||||
{
|
||||
manifestData = File.ReadAllBytes(manifestPath);
|
||||
|
|
@ -229,7 +238,12 @@ namespace FModel.ViewModels
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (Provider.MountedVfs.FirstOrDefault(x => x.Name == file.Name) is not { } vfs)
|
||||
{
|
||||
if (Provider.UnloadedVfs.FirstOrDefault(x => x.Name == file.Name) is IoStoreReader store)
|
||||
file.FileCount = (int)store.Info.TocEntryCount - 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
file.IsEnabled = true;
|
||||
file.MountPoint = vfs.MountPoint;
|
||||
|
|
@ -320,32 +334,68 @@ namespace FModel.ViewModels
|
|||
}
|
||||
|
||||
public async Task LoadLocalizedResources()
|
||||
{
|
||||
await LoadGameLocalizedResources();
|
||||
await LoadHotfixedLocalizedResources();
|
||||
if (LocalizedResourcesCount > 0)
|
||||
{
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"{LocalizedResourcesCount} localized resources loaded for '{UserSettings.Default.AssetLanguage.GetDescription()}'", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"Could not load localized resources in '{UserSettings.Default.AssetLanguage.GetDescription()}', language may not exist", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadGameLocalizedResources()
|
||||
{
|
||||
if (LocalizedResourcesCount > 0) return;
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
LocalizedResourcesCount = Provider.LoadLocalization(UserSettings.Default.AssetLanguage, cancellationToken);
|
||||
if (LocalizedResourcesCount > 0)
|
||||
{
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"{LocalizedResourcesCount} localized resources loaded for '{UserSettings.Default.AssetLanguage.GetDescription()}'", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"Could not load localized resources in '{UserSettings.Default.AssetLanguage.GetDescription()}', language may not exist", Constants.WHITE, true);
|
||||
}
|
||||
|
||||
Utils.Typefaces = new Typefaces(this);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load hotfixed localized resources
|
||||
/// </summary>
|
||||
/// <remarks>Functions only when LoadLocalizedResources is used prior to this (Asval: Why?).</remarks>
|
||||
private async Task LoadHotfixedLocalizedResources()
|
||||
{
|
||||
if (Game != FGame.FortniteGame) return;
|
||||
|
||||
if (HotfixedResourcesDone) return;
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
var hotfixes = ApplicationService.ApiEndpointView.BenbotApi.GetHotfixes(cancellationToken, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage));
|
||||
if (hotfixes == null) return;
|
||||
|
||||
HotfixedResourcesDone = true;
|
||||
foreach (var entries in hotfixes)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!Provider.LocalizedResources.ContainsKey(entries.Key))
|
||||
Provider.LocalizedResources[entries.Key] = new Dictionary<string, string>();
|
||||
|
||||
foreach (var keyValue in entries.Value)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Provider.LocalizedResources[entries.Key][keyValue.Key] = keyValue.Value;
|
||||
LocalizedResourcesCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LoadVirtualPaths()
|
||||
{
|
||||
if (VirtualPathCount > 0) return;
|
||||
await _threadWorkerView.Begin(cancellationToken =>
|
||||
{
|
||||
VirtualPathCount = Provider.LoadVirtualPaths(cancellationToken);
|
||||
VirtualPathCount = Provider.LoadVirtualPaths(UserSettings.Default.OverridedUEVersion[Game], cancellationToken);
|
||||
if (VirtualPathCount > 0)
|
||||
{
|
||||
FLogger.AppendInformation();
|
||||
|
|
@ -365,7 +415,7 @@ namespace FModel.ViewModels
|
|||
{
|
||||
Thread.Sleep(10);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Extract(asset.FullPath, TabControl.HasNoTabs);
|
||||
try {Extract(asset.FullPath, TabControl.HasNoTabs);} catch {/**/}
|
||||
}
|
||||
|
||||
foreach (var f in folder.Folders) ExtractFolder(cancellationToken, f);
|
||||
|
|
@ -389,7 +439,7 @@ namespace FModel.ViewModels
|
|||
{
|
||||
Thread.Sleep(10);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Extract(asset.FullPath, TabControl.HasNoTabs, true);
|
||||
try {Extract(asset.FullPath, TabControl.HasNoTabs, true);} catch {/**/}
|
||||
}
|
||||
|
||||
foreach (var f in folder.Folders) SaveFolder(cancellationToken, f);
|
||||
|
|
@ -443,6 +493,11 @@ namespace FModel.ViewModels
|
|||
case "log":
|
||||
case "po":
|
||||
case "bat":
|
||||
case "dat":
|
||||
case "cfg":
|
||||
case "ide":
|
||||
case "ipl":
|
||||
case "zon":
|
||||
case "xml":
|
||||
case "h":
|
||||
case "uproject":
|
||||
|
|
@ -570,7 +625,7 @@ namespace FModel.ViewModels
|
|||
default:
|
||||
{
|
||||
FLogger.AppendWarning();
|
||||
FLogger.AppendText($"The file '{fileName}' is of an unknown type. If this is a mistake, we are already working to fix it!", Constants.WHITE, true);
|
||||
FLogger.AppendText($"The file '{fileName}' is of an unknown type.", Constants.WHITE, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -617,36 +672,33 @@ namespace FModel.ViewModels
|
|||
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
|
||||
return false;
|
||||
}
|
||||
case UStaticMesh:
|
||||
case USkeletalMesh:
|
||||
case UMaterialInterface when UserSettings.Default.IsAutoSaveMaterials: // don't trigger model viewer if false
|
||||
case UAnimSequence when UserSettings.Default.IsAutoSaveAnimations: // don't trigger model viewer if false
|
||||
case UStaticMesh when UserSettings.Default.IsAutoOpenMeshes:
|
||||
case USkeletalMesh when UserSettings.Default.IsAutoOpenMeshes:
|
||||
case UMaterialInstance when UserSettings.Default.IsAutoOpenMeshes && !ModelIsSwappingMaterial &&
|
||||
!(Game == FGame.FortniteGame && export.Owner != null && (export.Owner.Name.EndsWith($"/MI_OfferImages/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
|
||||
export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) ||
|
||||
export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))):
|
||||
{
|
||||
if (UserSettings.Default.IsAutoSaveMaterials || UserSettings.Default.IsAutoSaveMeshes || UserSettings.Default.IsAutoSaveAnimations)
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
var toSave = new Exporter(export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat);
|
||||
var toSaveDirectory = new DirectoryInfo(Path.Combine(UserSettings.Default.OutputDirectory, "Saves"));
|
||||
if (toSave.TryWriteToDir(toSaveDirectory, out var savedFileName))
|
||||
{
|
||||
Log.Information("Successfully saved {FileName}", savedFileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", savedFileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
|
||||
modelViewer.Load(export);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
case UMaterialInstance m when ModelIsSwappingMaterial:
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
|
||||
modelViewer.Load(export);
|
||||
});
|
||||
}
|
||||
var modelViewer = Helper.GetWindow<ModelViewer>("Model Viewer", () => new ModelViewer().Show());
|
||||
modelViewer.Swap(m);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
case USkeleton when UserSettings.Default.SaveSkeletonAsMesh:
|
||||
case UAnimSequence when UserSettings.Default.IsAutoSaveAnimations:
|
||||
{
|
||||
SaveExport(export);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
|
@ -666,8 +718,7 @@ namespace FModel.ViewModels
|
|||
var userDir = Path.Combine(UserSettings.Default.OutputDirectory, "Sounds");
|
||||
if (fullPath.StartsWith("/")) fullPath = fullPath[1..];
|
||||
var savedAudioPath = Path.Combine(userDir,
|
||||
UserSettings.Default.KeepDirectoryStructure == EEnabledDisabled.Enabled
|
||||
? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLower()}";
|
||||
UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLower()}";
|
||||
|
||||
if (!UserSettings.Default.IsAutoOpenSounds)
|
||||
{
|
||||
|
|
@ -690,6 +741,24 @@ namespace FModel.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private void SaveExport(UObject export)
|
||||
{
|
||||
var toSave = new Exporter(export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat, UserSettings.Default.MeshExportFormat);
|
||||
var toSaveDirectory = new DirectoryInfo(Path.Combine(UserSettings.Default.OutputDirectory, "Saves"));
|
||||
if (toSave.TryWriteToDir(toSaveDirectory, out var savedFileName))
|
||||
{
|
||||
Log.Information("Successfully saved {FileName}", savedFileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", savedFileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExportData(string fullPath)
|
||||
{
|
||||
var fileName = fullPath.SubstringAfterLast('/');
|
||||
|
|
@ -699,8 +768,7 @@ namespace FModel.ViewModels
|
|||
{
|
||||
foreach (var kvp in assets)
|
||||
{
|
||||
var path = Path.Combine(directory, UserSettings.Default.KeepDirectoryStructure == EEnabledDisabled.Enabled
|
||||
? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/');
|
||||
var path = Path.Combine(directory, UserSettings.Default.KeepDirectoryStructure ? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/');
|
||||
Directory.CreateDirectory(path.SubstringBeforeLast('/'));
|
||||
File.WriteAllBytes(path, kvp.Value);
|
||||
}
|
||||
|
|
@ -717,4 +785,4 @@ namespace FModel.ViewModels
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
|
@ -45,14 +45,14 @@ namespace FModel.ViewModels.Commands
|
|||
FLogger.AppendText("An encrypted file has been found. In order to decrypt it, please specify a working AES encryption key", Constants.WHITE, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_applicationView.CUE4Parse.Game == FGame.FortniteGame &&
|
||||
_applicationView.CUE4Parse.Provider.MappingsContainer == null)
|
||||
{
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText("Mappings could not get pulled, extracting assets might not work properly. If so, press F12 or please restart.", Constants.WHITE, true);
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
var loadingTime = Stopwatch.StartNew();
|
||||
#endif
|
||||
|
|
@ -225,4 +225,4 @@ namespace FModel.ViewModels.Commands
|
|||
_applicationView.CUE4Parse.AssetsFolder.BulkPopulate(entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using AdonisUI.Controls;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.Views;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FModel.ViewModels.Commands
|
||||
{
|
||||
|
|
@ -28,6 +30,11 @@ namespace FModel.ViewModels.Commands
|
|||
case "Directory_Backup":
|
||||
Helper.OpenWindow<AdonisWindow>("Backup Manager", () => new BackupManager(contextViewModel.CUE4Parse.Provider.GameName).Show());
|
||||
break;
|
||||
case "Directory_ArchivesInfo":
|
||||
contextViewModel.CUE4Parse.TabControl.AddTab("Archives Info");
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json");
|
||||
contextViewModel.CUE4Parse.TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(contextViewModel.CUE4Parse.GameDirectory.DirectoryFiles, Formatting.Indented), false);
|
||||
break;
|
||||
case "Views_AudioPlayer":
|
||||
Helper.OpenWindow<AdonisWindow>("Audio Player", () => new AudioPlayer().Show());
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using FModel.Extensions;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
|
@ -7,6 +7,8 @@ using System.Collections.Generic;
|
|||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace FModel.ViewModels
|
||||
{
|
||||
|
|
@ -43,14 +45,14 @@ namespace FModel.ViewModels
|
|||
|
||||
public void AddUnknownGame(string gameDirectory)
|
||||
{
|
||||
_autoDetectedGames.Add(new DetectedGame {GameName = gameDirectory.SubstringAfterLast('\\'), GameDirectory = gameDirectory});
|
||||
_autoDetectedGames.Add(new DetectedGame { GameName = gameDirectory.SubstringAfterLast('\\'), GameDirectory = gameDirectory });
|
||||
SelectedDetectedGame = AutoDetectedGames.Last();
|
||||
}
|
||||
|
||||
private IEnumerable<DetectedGame> EnumerateDetectedGames()
|
||||
{
|
||||
yield return GetUnrealEngineGame("Fortnite", "\\FortniteGame\\Content\\Paks");
|
||||
yield return new DetectedGame {GameName = "Fortnite [LIVE]", GameDirectory = Constants._FN_LIVE_TRIGGER};
|
||||
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");
|
||||
|
|
@ -62,20 +64,25 @@ namespace FModel.ViewModels
|
|||
yield return GetUnrealEngineGame("a99769d95d8f400baad1f67ab5dfe508", "\\Core\\Platform\\Content\\Paks");
|
||||
yield return GetUnrealEngineGame("Nebula", "\\BendGame\\Content");
|
||||
yield return GetRiotGame("VALORANT", "ShooterGame\\Content\\Paks");
|
||||
yield return new DetectedGame {GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER};
|
||||
yield return new DetectedGame { GameName = "Valorant [LIVE]", GameDirectory = Constants._VAL_LIVE_TRIGGER };
|
||||
yield return GetMojangGame("MinecraftDungeons", "\\dungeons\\dungeons\\Dungeons\\Content\\Paks");
|
||||
yield return GetSteamGame(578080, "\\TslGame\\Content\\Paks"); // PUBG
|
||||
yield return GetSteamGame(677620, "\\PortalWars\\Content\\Paks"); // Splitgate
|
||||
yield return GetRockstarGamesGame("GTA III - Definitive Edition", "\\Gameface\\Content\\Paks");
|
||||
yield return GetRockstarGamesGame("GTA San Andreas - Definitive Edition", "\\Gameface\\Content\\Paks");
|
||||
yield return GetRockstarGamesGame("GTA Vice City - Definitive Edition", "\\Gameface\\Content\\Paks");
|
||||
}
|
||||
|
||||
private LauncherInstalled _launcherInstalled;
|
||||
private DetectedGame GetUnrealEngineGame(string gameName, string pakDirectory)
|
||||
{
|
||||
_launcherInstalled ??= GetDrivedLauncherInstalls<LauncherInstalled>("ProgramData\\Epic\\UnrealEngineLauncher\\LauncherInstalled.dat");
|
||||
_launcherInstalled ??= GetDriveLauncherInstalls<LauncherInstalled>("ProgramData\\Epic\\UnrealEngineLauncher\\LauncherInstalled.dat");
|
||||
if (_launcherInstalled?.InstallationList != null)
|
||||
{
|
||||
foreach (var installationList in _launcherInstalled.InstallationList)
|
||||
{
|
||||
if (installationList.AppName.Equals(gameName, StringComparison.OrdinalIgnoreCase))
|
||||
return new DetectedGame {GameName = installationList.AppName, GameDirectory = $"{installationList.InstallLocation}{pakDirectory}"};
|
||||
return new DetectedGame { GameName = installationList.AppName, GameDirectory = $"{installationList.InstallLocation}{pakDirectory}" };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +93,8 @@ namespace FModel.ViewModels
|
|||
private RiotClientInstalls _riotClientInstalls;
|
||||
private DetectedGame GetRiotGame(string gameName, string pakDirectory)
|
||||
{
|
||||
_riotClientInstalls ??= GetDrivedLauncherInstalls<RiotClientInstalls>("ProgramData\\Riot Games\\RiotClientInstalls.json");
|
||||
if (_riotClientInstalls is {AssociatedClient: { }})
|
||||
_riotClientInstalls ??= GetDriveLauncherInstalls<RiotClientInstalls>("ProgramData\\Riot Games\\RiotClientInstalls.json");
|
||||
if (_riotClientInstalls is { AssociatedClient: { } })
|
||||
{
|
||||
foreach (var (key, _) in _riotClientInstalls.AssociatedClient)
|
||||
{
|
||||
|
|
@ -101,24 +108,52 @@ namespace FModel.ViewModels
|
|||
}
|
||||
|
||||
private LauncherSettings _launcherSettings;
|
||||
|
||||
private DetectedGame GetMojangGame(string gameName, string pakDirectory)
|
||||
{
|
||||
_launcherSettings ??= GetDataLauncherInstalls<LauncherSettings>("\\.minecraft\\launcher_settings.json");
|
||||
if (_launcherSettings is {ProductLibraryDir: { }})
|
||||
return new DetectedGame {GameName = gameName, GameDirectory = $"{_launcherSettings.ProductLibraryDir}{pakDirectory}"};
|
||||
if (_launcherSettings is { ProductLibraryDir: { } })
|
||||
return new DetectedGame { GameName = gameName, GameDirectory = $"{_launcherSettings.ProductLibraryDir}{pakDirectory}" };
|
||||
|
||||
Log.Warning("Could not find {GameName} in launcher_settings.json", gameName);
|
||||
return null;
|
||||
}
|
||||
|
||||
private T GetDrivedLauncherInstalls<T>(string jsonFile)
|
||||
private DetectedGame GetSteamGame(int id, string pakDirectory)
|
||||
{
|
||||
var steamInfo = SteamDetection.GetSteamGameById(id);
|
||||
if (steamInfo is not null)
|
||||
return new DetectedGame { GameName = steamInfo.Name, GameDirectory = $"{steamInfo.GameRoot}{pakDirectory}" };
|
||||
|
||||
Log.Warning("Could not find {GameId} in steam manifests", id);
|
||||
return null;
|
||||
}
|
||||
|
||||
private DetectedGame GetRockstarGamesGame(string key, string pakDirectory)
|
||||
{
|
||||
var installLocation = string.Empty;
|
||||
try
|
||||
{
|
||||
installLocation = App.GetRegistryValue(@$"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{key}", "InstallLocation", RegistryHive.LocalMachine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(installLocation))
|
||||
return new DetectedGame { GameName = key, GameDirectory = $"{installLocation}{pakDirectory}" };
|
||||
|
||||
Log.Warning("Could not find {GameName} in the registry", key);
|
||||
return null;
|
||||
}
|
||||
|
||||
private T GetDriveLauncherInstalls<T>(string jsonFile)
|
||||
{
|
||||
foreach (var drive in DriveInfo.GetDrives())
|
||||
{
|
||||
var launcher = $"{drive.Name}{jsonFile}";
|
||||
if (!File.Exists(launcher)) continue;
|
||||
|
||||
|
||||
Log.Information("\"{Launcher}\" found in drive \"{DriveName}\"", launcher, drive.Name);
|
||||
return JsonConvert.DeserializeObject<T>(File.ReadAllText(launcher));
|
||||
}
|
||||
|
|
@ -158,10 +193,13 @@ namespace FModel.ViewModels
|
|||
{
|
||||
[JsonProperty("associated_client", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, string> AssociatedClient;
|
||||
|
||||
[JsonProperty("patchlines", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public Dictionary<string, string> Patchlines;
|
||||
|
||||
[JsonProperty("rc_default", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RcDefault;
|
||||
|
||||
[JsonProperty("rc_live", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RcLive;
|
||||
}
|
||||
|
|
@ -170,17 +208,113 @@ namespace FModel.ViewModels
|
|||
{
|
||||
[JsonProperty("channel", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Channel;
|
||||
|
||||
[JsonProperty("customChannels", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public object[] CustomChannels;
|
||||
|
||||
[JsonProperty("deviceId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DeviceId;
|
||||
|
||||
[JsonProperty("formatVersion", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int FormatVersion;
|
||||
|
||||
[JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Locale;
|
||||
|
||||
[JsonProperty("productLibraryDir", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ProductLibraryDir;
|
||||
}
|
||||
#pragma warning restore 649
|
||||
|
||||
// https://stackoverflow.com/questions/54767662/finding-game-launcher-executables-in-directory-c-sharp/67679123#67679123
|
||||
public static class SteamDetection
|
||||
{
|
||||
private static readonly List<AppInfo> _steamApps;
|
||||
|
||||
static SteamDetection()
|
||||
{
|
||||
_steamApps = GetSteamApps(GetSteamLibs());
|
||||
}
|
||||
|
||||
public static AppInfo GetSteamGameById(int id) => _steamApps.FirstOrDefault(app => app.Id == id.ToString());
|
||||
|
||||
private static List<AppInfo> GetSteamApps(IEnumerable<string> steamLibs)
|
||||
{
|
||||
var apps = new List<AppInfo>();
|
||||
foreach (var files in steamLibs.Select(lib => Path.Combine(lib, "SteamApps")).Select(appMetaDataPath => Directory.GetFiles(appMetaDataPath, "*.acf")))
|
||||
{
|
||||
apps.AddRange(files.Select(GetAppInfo).Where(appInfo => appInfo != null));
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
private static AppInfo GetAppInfo(string appMetaFile)
|
||||
{
|
||||
var fileDataLines = File.ReadAllLines(appMetaFile);
|
||||
var dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var line in fileDataLines)
|
||||
{
|
||||
var match = Regex.Match(line, @"\s*""(?<key>\w+)""\s+""(?<val>.*)""");
|
||||
if (!match.Success) continue;
|
||||
var key = match.Groups["key"].Value;
|
||||
var val = match.Groups["val"].Value;
|
||||
dic[key] = val;
|
||||
}
|
||||
|
||||
if (dic.Keys.Count <= 0) return null;
|
||||
AppInfo appInfo = new();
|
||||
var appId = dic["appid"];
|
||||
var name = dic["name"];
|
||||
var installDir = dic["installDir"];
|
||||
|
||||
var path = Path.GetDirectoryName(appMetaFile);
|
||||
var libGameRoot = Path.Combine(path, "common", installDir);
|
||||
|
||||
if (!Directory.Exists(libGameRoot)) return null;
|
||||
|
||||
appInfo.Id = appId;
|
||||
appInfo.Name = name;
|
||||
appInfo.GameRoot = libGameRoot;
|
||||
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
private static List<string> GetSteamLibs()
|
||||
{
|
||||
var steamPath = GetSteamPath();
|
||||
if (steamPath == null) return new List<string>();
|
||||
var libraries = new List<string> { steamPath };
|
||||
|
||||
var listFile = Path.Combine(steamPath, @"steamapps\libraryfolders.vdf");
|
||||
var lines = File.ReadAllLines(listFile);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var match = Regex.Match(line, @"""(?<path>\w:\\\\.*)""");
|
||||
if (!match.Success) continue;
|
||||
var path = match.Groups["path"].Value.Replace(@"\\", @"\");
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
libraries.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
return libraries;
|
||||
}
|
||||
|
||||
private static string GetSteamPath() => (string) Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam", "InstallPath", ""); // Win64, we don't support Win32
|
||||
|
||||
public class AppInfo
|
||||
{
|
||||
public string Id { get; internal set; }
|
||||
public string Name { get; internal set; }
|
||||
public string GameRoot { get; internal set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name} ({Id})";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,49 +31,49 @@ namespace FModel.ViewModels
|
|||
Parkour,
|
||||
TimeTrials
|
||||
}
|
||||
|
||||
|
||||
public class MapViewerViewModel : ViewModel
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
private DiscordHandler _discordHandler => DiscordService.DiscordHandler;
|
||||
|
||||
#region BINDINGS
|
||||
|
||||
|
||||
private bool _brPois;
|
||||
public bool BrPois
|
||||
{
|
||||
get => _brPois;
|
||||
set => SetProperty(ref _brPois, value, "ApolloGameplay_MapPois");
|
||||
}
|
||||
|
||||
|
||||
private bool _brLandmarks;
|
||||
public bool BrLandmarks
|
||||
{
|
||||
get => _brLandmarks;
|
||||
set => SetProperty(ref _brLandmarks, value, "ApolloGameplay_MapLandmarks");
|
||||
}
|
||||
|
||||
|
||||
private bool _brTagsLocation;
|
||||
public bool BrTagsLocation
|
||||
{
|
||||
get => _brTagsLocation;
|
||||
set => SetProperty(ref _brTagsLocation, value, "ApolloGameplay_TagsLocation");
|
||||
}
|
||||
|
||||
|
||||
private bool _brPatrolsPath;
|
||||
public bool BrPatrolsPath
|
||||
{
|
||||
get => _brPatrolsPath;
|
||||
set => SetProperty(ref _brPatrolsPath, value, "ApolloGameplay_PatrolsPath");
|
||||
}
|
||||
|
||||
|
||||
private bool _brUpgradeBenches;
|
||||
public bool BrUpgradeBenches
|
||||
{
|
||||
get => _brUpgradeBenches;
|
||||
set => SetProperty(ref _brUpgradeBenches, value, "ApolloGameplay_UpgradeBenches");
|
||||
}
|
||||
|
||||
|
||||
private bool _brPhonebooths;
|
||||
public bool BrPhonebooths
|
||||
{
|
||||
|
|
@ -87,77 +87,70 @@ namespace FModel.ViewModels
|
|||
get => _brVendingMachines;
|
||||
set => SetProperty(ref _brVendingMachines, value, "ApolloGameplay_VendingMachines");
|
||||
}
|
||||
|
||||
|
||||
private bool _brFireflies;
|
||||
public bool BrFireflies
|
||||
{
|
||||
get => _brFireflies;
|
||||
set => SetProperty(ref _brFireflies, value, "ApolloGameplay_Fireflies");
|
||||
}
|
||||
|
||||
private bool _brCorruptionZones;
|
||||
public bool BrCorruptionZones
|
||||
{
|
||||
get => _brCorruptionZones;
|
||||
set => SetProperty(ref _brCorruptionZones, value, "ApolloGameplay_CorruptionZones");
|
||||
}
|
||||
|
||||
|
||||
private bool _brCubeMovements;
|
||||
public bool BrCubeMovements
|
||||
{
|
||||
get => _brCubeMovements;
|
||||
set => SetProperty(ref _brCubeMovements, value, "ApolloGameplay_CubeMovements");
|
||||
}
|
||||
|
||||
|
||||
private bool _prLandmarks;
|
||||
public bool PrLandmarks
|
||||
{
|
||||
get => _prLandmarks;
|
||||
set => SetProperty(ref _prLandmarks, value, "PapayaGameplay_MapLandmarks");
|
||||
}
|
||||
|
||||
|
||||
private bool _prCannonball;
|
||||
public bool PrCannonball
|
||||
{
|
||||
get => _prCannonball;
|
||||
set => SetProperty(ref _prCannonball, value, "PapayaGameplay_CannonballGame");
|
||||
}
|
||||
|
||||
|
||||
private bool _prSkydive;
|
||||
public bool PrSkydive
|
||||
{
|
||||
get => _prSkydive;
|
||||
set => SetProperty(ref _prSkydive, value, "PapayaGameplay_SkydiveGame");
|
||||
}
|
||||
|
||||
|
||||
private bool _prShootingTargets;
|
||||
public bool PrShootingTargets
|
||||
{
|
||||
get => _prShootingTargets;
|
||||
set => SetProperty(ref _prShootingTargets, value, "PapayaGameplay_ShootingTargets");
|
||||
}
|
||||
|
||||
|
||||
private bool _prParkour;
|
||||
public bool PrParkour
|
||||
{
|
||||
get => _prParkour;
|
||||
set => SetProperty(ref _prParkour, value, "PapayaGameplay_ParkourGame");
|
||||
}
|
||||
|
||||
|
||||
private bool _prTimeTrials;
|
||||
public bool PrTimeTrials
|
||||
{
|
||||
get => _prTimeTrials;
|
||||
set => SetProperty(ref _prTimeTrials, value, "PapayaGameplay_TimeTrials");
|
||||
}
|
||||
|
||||
|
||||
private bool _prVendingMachines;
|
||||
public bool PrVendingMachines
|
||||
{
|
||||
get => _prVendingMachines;
|
||||
set => SetProperty(ref _prVendingMachines, value, "PapayaGameplay_VendingMachines");
|
||||
}
|
||||
|
||||
|
||||
private bool _prMusicBlocks;
|
||||
public bool PrMusicBlocks
|
||||
{
|
||||
|
|
@ -177,7 +170,7 @@ namespace FModel.ViewModels
|
|||
get => _mapImage;
|
||||
set => SetProperty(ref _mapImage, value);
|
||||
}
|
||||
|
||||
|
||||
private BitmapImage _brLayerImage;
|
||||
private BitmapImage _prLayerImage;
|
||||
private BitmapImage _layerImage;
|
||||
|
|
@ -237,11 +230,7 @@ namespace FModel.ViewModels
|
|||
if (!value.IsEnabled || !withMap && key == _FIRST_BITMAP)
|
||||
continue;
|
||||
|
||||
SKPaint p = null;
|
||||
if (key == "ApolloGameplay_CorruptionZones")
|
||||
p = new SKPaint { BlendMode = SKBlendMode.Color };
|
||||
|
||||
c.DrawBitmap(value.Layer, new SKRect(0, 0, _widthHeight, _widthHeight), p);
|
||||
c.DrawBitmap(value.Layer, new SKRect(0, 0, _widthHeight, _widthHeight));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -253,7 +242,7 @@ namespace FModel.ViewModels
|
|||
if (bool.TryParse(value.ToString(), out var b)) GenericToggle(propertyName, b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private async void GenericToggle(string key, bool enabled)
|
||||
{
|
||||
if (_bitmaps[MapIndex].TryGetValue(key, out var layer) && layer.Layer != null)
|
||||
|
|
@ -287,9 +276,6 @@ namespace FModel.ViewModels
|
|||
case "ApolloGameplay_Fireflies":
|
||||
await LoadFireflies();
|
||||
break;
|
||||
case "ApolloGameplay_CorruptionZones":
|
||||
await LoadCorruptionZones();
|
||||
break;
|
||||
case "ApolloGameplay_CubeMovements":
|
||||
await LoadCubeMovements();
|
||||
break;
|
||||
|
|
@ -360,11 +346,11 @@ namespace FModel.ViewModels
|
|||
_layerImage = _prLayerImage;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
RaisePropertyChanged(nameof(MapImage));
|
||||
RaisePropertyChanged(nameof(LayerImage));
|
||||
}
|
||||
|
||||
|
||||
private readonly SKPaint _textPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
|
|
@ -389,12 +375,12 @@ namespace FModel.ViewModels
|
|||
var ny = (1 - (vector.X + mapRadius) / (mapRadius * 2)) * _widthHeight;
|
||||
return new FVector2D(nx, ny);
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadBrMiniMap()
|
||||
{
|
||||
if (_bitmaps[0].TryGetValue(_FIRST_BITMAP, out var brMap) && brMap.Layer != null)
|
||||
return; // if map already loaded
|
||||
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/UI/IngameMap/UIMapManagerBR.Default__UIMapManagerBR_C", out UObject mapManager) ||
|
||||
|
|
@ -412,7 +398,7 @@ namespace FModel.ViewModels
|
|||
{
|
||||
if (_bitmaps[1].TryGetValue(_FIRST_BITMAP, out var prMap) && prMap.Layer != null)
|
||||
return; // if map already loaded
|
||||
|
||||
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/UI/IngameMap/UIMapManagerPapaya.Default__UIMapManagerPapaya_C", out UObject mapManager) ||
|
||||
|
|
@ -423,7 +409,7 @@ namespace FModel.ViewModels
|
|||
_prMiniMapImage = GetImageSource(_bitmaps[1][_FIRST_BITMAP].Layer);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadQuestIndicatorData()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -434,7 +420,7 @@ namespace FModel.ViewModels
|
|||
using var pois = new SKCanvas(poisBitmap);
|
||||
using var brLandmarks = new SKCanvas(brLandmarksBitmap);
|
||||
using var prLandmarks = new SKCanvas(prLandmarksBitmap);
|
||||
|
||||
|
||||
if (Utils.TryLoadObject("FortniteGame/Content/Quests/QuestIndicatorData", out UObject indicatorData) &&
|
||||
indicatorData.TryGetValue(out FStructFallback[] challengeMapPoiData, "ChallengeMapPoiData"))
|
||||
{
|
||||
|
|
@ -444,7 +430,7 @@ namespace FModel.ViewModels
|
|||
!poiData.TryGetValue(out FText text, "Text") || string.IsNullOrEmpty(text.Text) ||
|
||||
!poiData.TryGetValue(out FVector worldLocation, "WorldLocation") ||
|
||||
!poiData.TryGetValue(out FName discoverBackend, "DiscoverObjectiveBackendName")) continue;
|
||||
|
||||
|
||||
var shaper = new CustomSKShaper(_textPaint.Typeface);
|
||||
var shapedText = shaper.Shape(text.Text, _textPaint);
|
||||
|
||||
|
|
@ -479,7 +465,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[1]["PapayaGameplay_MapLandmarks"] = new MapLayer {Layer = prLandmarksBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadPatrolsPath()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -514,7 +500,7 @@ namespace FModel.ViewModels
|
|||
vector = GetMapPosition(relativeLocation, _brRadius);
|
||||
path.LineTo(vector.X, vector.Y);
|
||||
}
|
||||
|
||||
|
||||
c.DrawPath(path, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
|
|
@ -544,7 +530,7 @@ namespace FModel.ViewModels
|
|||
|
||||
var displayName = Utils.GetLocalizedResource("", "D998BEF44F051E0885C6C58565934BEA", "Cannonball");
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
||||
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
|
|
@ -573,7 +559,7 @@ namespace FModel.ViewModels
|
|||
!uObject.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
|
||||
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(minigameActivityName.Text, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(minigameActivityName.Text, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
|
|
@ -582,7 +568,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[1]["PapayaGameplay_SkydiveGame"] = new MapLayer {Layer = skydiveBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadShootingTargets()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -604,7 +590,7 @@ namespace FModel.ViewModels
|
|||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (bDone) continue;
|
||||
|
||||
|
||||
bDone = true;
|
||||
c.DrawText("Shooting Target", vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText("Shooting Target", vector.X, vector.Y - 12.5F, _textPaint);
|
||||
|
|
@ -637,7 +623,7 @@ namespace FModel.ViewModels
|
|||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
|
||||
|
||||
var path = new SKPath();
|
||||
var exports = Utils.LoadExports($"/PapayaGameplay/LevelOverlays/{file}");
|
||||
foreach (var export in exports)
|
||||
|
|
@ -647,7 +633,7 @@ namespace FModel.ViewModels
|
|||
if (!export.TryGetValue(out FPackageIndex rootComponent, "RootComponent") ||
|
||||
!Utils.TryGetPackageIndexExport(rootComponent, out UObject root) ||
|
||||
!root.TryGetValue(out FVector relativeLocation, "RelativeLocation")) continue;
|
||||
|
||||
|
||||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
if (path.IsEmpty || export.TryGetValue(out bool startsTrial, "StartsTrial") && startsTrial)
|
||||
{
|
||||
|
|
@ -703,7 +689,7 @@ namespace FModel.ViewModels
|
|||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (!set.Add(name)) continue;
|
||||
|
||||
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText(name, vector.X, vector.Y - 12.5F, _textPaint);
|
||||
}
|
||||
|
|
@ -711,7 +697,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[1]["PapayaGameplay_VendingMachines"] = new MapLayer {Layer = timeTrialsBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadMusicBlocks()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -733,7 +719,7 @@ namespace FModel.ViewModels
|
|||
var vector = GetMapPosition(relativeLocation, _prRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
if (bDone) continue;
|
||||
|
||||
|
||||
bDone = true;
|
||||
c.DrawText("Music Blocks", vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
c.DrawText("Music Blocks", vector.X, vector.Y - 12.5F, _textPaint);
|
||||
|
|
@ -742,7 +728,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[1]["PapayaGameplay_MusicBlocks"] = new MapLayer {Layer = shootingTargetsBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadUpgradeBenches()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -770,7 +756,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[0]["ApolloGameplay_UpgradeBenches"] = new MapLayer {Layer = upgradeBenchesBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadPhonebooths()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -798,7 +784,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[0]["ApolloGameplay_Phonebooths"] = new MapLayer {Layer = phoneboothsBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadBrVendingMachines()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -827,7 +813,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[0]["ApolloGameplay_VendingMachines"] = new MapLayer {Layer = vendingMachinesBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadFireflies()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
|
|
@ -863,7 +849,7 @@ namespace FModel.ViewModels
|
|||
_fillPaint.StrokeWidth = 5;
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Quests/QuestTagToLocationDataRows.QuestTagToLocationDataRows", out UDataTable locationData))
|
||||
return;
|
||||
|
||||
|
||||
var tagsLocationBitmap = new SKBitmap(_widthHeight, _widthHeight, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(tagsLocationBitmap);
|
||||
|
||||
|
|
@ -878,7 +864,7 @@ namespace FModel.ViewModels
|
|||
var displayName = parts[^2];
|
||||
if (!int.TryParse(parts[^1], out var _))
|
||||
displayName += " " + parts[^1];
|
||||
|
||||
|
||||
var vector = GetMapPosition(worldLocation, _brRadius);
|
||||
c.DrawPoint(vector.X, vector.Y, _pathPaint);
|
||||
c.DrawText(displayName, vector.X, vector.Y - 12.5F, _fillPaint);
|
||||
|
|
@ -888,38 +874,7 @@ namespace FModel.ViewModels
|
|||
_bitmaps[0]["ApolloGameplay_TagsLocation"] = new MapLayer {Layer = tagsLocationBitmap, IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadCorruptionZones()
|
||||
{
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
_fillPaint.StrokeWidth = 5;
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Apollo/Environments/Landscape/Materials/Corruption/T_InitialCorruptionAreas.T_InitialCorruptionAreas", out UTexture2D corruption))
|
||||
return;
|
||||
|
||||
var overlay = Utils.GetBitmap(corruption);
|
||||
var width = overlay.Width;
|
||||
var height = overlay.Height;
|
||||
var rotatedBitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
|
||||
using var c = new SKCanvas(rotatedBitmap);
|
||||
c.Clear();
|
||||
c.Translate(0, width);
|
||||
c.RotateDegrees(-90);
|
||||
c.DrawRect(0, 0, width, height, new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateCompose(SKShader.CreateSweepGradient(new SKPoint(width / 2f, height / 2f),new [] {
|
||||
SKColor.Parse("#352176"), SKColor.Parse("#fd78fa"), SKColor.Parse("#f0b843"), SKColor.Parse("#e54a21")
|
||||
}, null), SKShader.CreatePerlinNoiseTurbulence(0.05f, 0.05f, 4, 0), SKBlendMode.SrcOver)
|
||||
});
|
||||
c.DrawBitmap(overlay, 0, 0, new SKPaint { BlendMode = SKBlendMode.Darken });
|
||||
rotatedBitmap.ClearToTransparent();
|
||||
|
||||
_bitmaps[0]["ApolloGameplay_CorruptionZones"] = new MapLayer {Layer = rotatedBitmap.Resize(_widthHeight, _widthHeight), IsEnabled = false};
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FortniteGame/Plugins/GameFeatures/CorruptionGameplay/Content/CorruptionGameplay_LevelOverlay.uasset
|
||||
/// too lazy to filters
|
||||
|
|
@ -944,7 +899,7 @@ namespace FModel.ViewModels
|
|||
var objectName = cubeMovementStaticPath.SubPathString.SubstringAfterLast(".");
|
||||
if (!Utils.TryLoadObject($"{objectPath}.{objectName}", out UObject staticPath))
|
||||
return;
|
||||
|
||||
|
||||
DrawCubeMovements(c, staticPath, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media.Media3D;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
|
|
@ -10,44 +16,55 @@ using CUE4Parse.UE4.Objects.Core.Math;
|
|||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Meshes.PSK;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using HelixToolkit.SharpDX.Core;
|
||||
using HelixToolkit.Wpf.SharpDX;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using Serilog;
|
||||
using SharpDX;
|
||||
using SkiaSharp;
|
||||
using Camera = HelixToolkit.Wpf.SharpDX.Camera;
|
||||
using Geometry3D = HelixToolkit.SharpDX.Core.Geometry3D;
|
||||
using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
|
||||
|
||||
namespace FModel.ViewModels
|
||||
{
|
||||
public class ModelViewerViewModel : ViewModel
|
||||
{
|
||||
private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;
|
||||
|
||||
#region BINDINGS
|
||||
private EffectsManager _effectManager;
|
||||
public EffectsManager EffectManager
|
||||
{
|
||||
get => _effectManager;
|
||||
set => SetProperty(ref _effectManager, value);
|
||||
}
|
||||
|
||||
|
||||
private Camera _cam;
|
||||
public Camera Cam
|
||||
{
|
||||
get => _cam;
|
||||
set => SetProperty(ref _cam, value);
|
||||
}
|
||||
|
||||
|
||||
private Geometry3D _xAxis;
|
||||
public Geometry3D XAxis
|
||||
{
|
||||
get => _xAxis;
|
||||
set => SetProperty(ref _xAxis, value);
|
||||
}
|
||||
|
||||
|
||||
private Geometry3D _yAxis;
|
||||
public Geometry3D YAxis
|
||||
{
|
||||
get => _yAxis;
|
||||
set => SetProperty(ref _yAxis, value);
|
||||
}
|
||||
|
||||
|
||||
private Geometry3D _zAxis;
|
||||
public Geometry3D ZAxis
|
||||
{
|
||||
|
|
@ -55,162 +72,469 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _zAxis, value);
|
||||
}
|
||||
|
||||
private bool _showWireframe;
|
||||
public bool ShowWireframe
|
||||
private ModelAndCam _selectedModel; // selected mesh
|
||||
public ModelAndCam SelectedModel
|
||||
{
|
||||
get => _showWireframe;
|
||||
set => SetProperty(ref _showWireframe, value);
|
||||
get => _selectedModel;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedModel, value);
|
||||
if (_selectedModel == null) return;
|
||||
|
||||
XAxis = _selectedModel.XAxis;
|
||||
YAxis = _selectedModel.YAxis;
|
||||
ZAxis = _selectedModel.ZAxis;
|
||||
Cam.UpDirection = new Vector3D(0, 1, 0);
|
||||
Cam.Position = _selectedModel.Position;
|
||||
Cam.LookDirection = _selectedModel.LookDirection;
|
||||
}
|
||||
}
|
||||
|
||||
private Geometry3D _mesh;
|
||||
public Geometry3D Mesh
|
||||
{
|
||||
get => _mesh;
|
||||
set => SetProperty(ref _mesh, value);
|
||||
}
|
||||
|
||||
private Material _meshMat;
|
||||
public Material MeshMat
|
||||
{
|
||||
get => _meshMat;
|
||||
set => SetProperty(ref _meshMat, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<Geometry3D> Lods { get; }
|
||||
private readonly ObservableCollection<ModelAndCam> _loadedModels; // mesh list
|
||||
public ICollectionView LoadedModelsView { get; }
|
||||
|
||||
public ModelViewerViewModel()
|
||||
private bool _appendMode;
|
||||
public bool AppendMode
|
||||
{
|
||||
Lods = new ObservableCollection<Geometry3D>();
|
||||
get => _appendMode;
|
||||
set => SetProperty(ref _appendMode, value);
|
||||
}
|
||||
|
||||
public bool CanAppend => SelectedModel != null;
|
||||
|
||||
public TextureModel HDRi { get; private set; }
|
||||
#endregion
|
||||
|
||||
private readonly FGame _game;
|
||||
private readonly int[] _facesIndex = { 1, 0, 2 };
|
||||
|
||||
public ModelViewerViewModel(FGame game)
|
||||
{
|
||||
_game = game;
|
||||
_loadedModels = new ObservableCollection<ModelAndCam>();
|
||||
|
||||
EffectManager = new DefaultEffectsManager();
|
||||
Cam = new PerspectiveCamera
|
||||
{
|
||||
NearPlaneDistance = 0.1,
|
||||
FarPlaneDistance = 10000000,
|
||||
FieldOfView = 80
|
||||
};
|
||||
LoadedModelsView = new ListCollectionView(_loadedModels);
|
||||
Cam = new PerspectiveCamera { NearPlaneDistance = 0.1, FarPlaneDistance = double.PositiveInfinity, FieldOfView = 90 };
|
||||
LoadHDRi();
|
||||
}
|
||||
|
||||
public void NextLod() => Mesh = Lods.Next(Mesh);
|
||||
public void PreviousLod() => Mesh = Lods.Previous(Mesh);
|
||||
|
||||
public void LoadExport(UObject export)
|
||||
private void LoadHDRi()
|
||||
{
|
||||
Lods.Clear();
|
||||
Mesh = null;
|
||||
MeshMat = PhongMaterials.Bisque;
|
||||
|
||||
switch (export)
|
||||
{
|
||||
case UStaticMesh st:
|
||||
LoadStaticMesh(st);
|
||||
break;
|
||||
case USkeletalMesh sk:
|
||||
LoadSkeletalMesh(sk);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var cubeMap = Application.GetResourceStream(new Uri("/FModel;component/Resources/approaching_storm_cubemap.dds", UriKind.Relative));
|
||||
HDRi = TextureModel.Create(cubeMap?.Stream);
|
||||
}
|
||||
|
||||
private void LoadStaticMesh(UStaticMesh mesh)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox.Min, convertedMesh.BoundingBox.Max);
|
||||
|
||||
var pushedMaterial = false;
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
public async Task LoadExport(UObject export)
|
||||
{
|
||||
#if DEBUG
|
||||
LoadHDRi();
|
||||
#endif
|
||||
|
||||
ModelAndCam p;
|
||||
if (AppendMode && CanAppend)
|
||||
p = SelectedModel;
|
||||
else
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
|
||||
PushLod(lod.Verts, lod.Indices.Value);
|
||||
if (!pushedMaterial)
|
||||
p = new ModelAndCam(export);
|
||||
_loadedModels.Add(p);
|
||||
}
|
||||
|
||||
bool valid = false;
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
valid = export switch
|
||||
{
|
||||
PushMaterial(lod.Sections.Value);
|
||||
pushedMaterial = true;
|
||||
}
|
||||
}
|
||||
Mesh = Lods.First();
|
||||
}
|
||||
|
||||
private void LoadSkeletalMesh(USkeletalMesh mesh)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox.Min, convertedMesh.BoundingBox.Max);
|
||||
|
||||
var pushedMaterial = false;
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
|
||||
PushLod(lod.Verts, lod.Indices.Value);
|
||||
if (!pushedMaterial)
|
||||
{
|
||||
PushMaterial(lod.Sections.Value);
|
||||
pushedMaterial = true;
|
||||
}
|
||||
}
|
||||
Mesh = Lods.First();
|
||||
UStaticMesh st => TryLoadStaticMesh(st, p),
|
||||
USkeletalMesh sk => TryLoadSkeletalMesh(sk, p),
|
||||
UMaterialInstance mi => TryLoadMaterialInstance(mi, p),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(export))
|
||||
};
|
||||
});
|
||||
if (!valid) return;
|
||||
SelectedModel = p;
|
||||
}
|
||||
|
||||
private void PushLod(CMeshVertex[] verts, FRawStaticIndexBuffer indices)
|
||||
#region PUBLIC METHODS
|
||||
public void RenderingToggle()
|
||||
{
|
||||
var builder = new MeshBuilder {TextureCoordinates = new Vector2Collection()};
|
||||
for (var i = 0; i < verts.Length; i++)
|
||||
if (SelectedModel == null) return;
|
||||
foreach (var g in SelectedModel.Group3d)
|
||||
{
|
||||
builder.AddNode(
|
||||
new Vector3(verts[i].Position.X, -verts[i].Position.Y, verts[i].Position.Z),
|
||||
new Vector3(verts[i].Normal.X, verts[i].Normal.Y, verts[i].Normal.Z),
|
||||
new Vector2(verts[i].UV.U, verts[i].UV.V));
|
||||
}
|
||||
|
||||
for (var i = 0; i < indices.Length; i++)
|
||||
{
|
||||
builder.TriangleIndices.Add(indices[i]);
|
||||
}
|
||||
|
||||
Lods.Add(builder.ToMesh());
|
||||
}
|
||||
|
||||
private void PushMaterial(CMeshSection[] sections)
|
||||
{
|
||||
for (var j = 0; j < sections.Length; j++)
|
||||
{
|
||||
if (sections[j].Material == null || !sections[j].Material.TryLoad<UMaterialInterface>(out var unrealMaterial))
|
||||
if (g is not MeshGeometryModel3D geometryModel)
|
||||
continue;
|
||||
|
||||
var parameters = new CMaterialParams();
|
||||
unrealMaterial.GetParams(parameters);
|
||||
if (parameters.Diffuse is not UTexture2D diffuse) continue;
|
||||
MeshMat = new DiffuseMaterial {DiffuseMap = new TextureModel(diffuse.Decode()?.Encode().AsStream())};
|
||||
break;
|
||||
geometryModel.IsRendering = !geometryModel.IsRendering;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupCameraAndAxis(FVector min, FVector max)
|
||||
public void WirefreameToggle()
|
||||
{
|
||||
var minOfMin = min.Min();
|
||||
var maxOfMax = max.Max();
|
||||
Cam.UpDirection = new System.Windows.Media.Media3D.Vector3D(0, 0, 1);
|
||||
Cam.Position = new System.Windows.Media.Media3D.Point3D(maxOfMax, maxOfMax, (minOfMin + maxOfMax) / 1.25);
|
||||
Cam.LookDirection = new System.Windows.Media.Media3D.Vector3D(-Cam.Position.X, -Cam.Position.Y, 0);
|
||||
|
||||
if (SelectedModel == null) return;
|
||||
foreach (var g in SelectedModel.Group3d)
|
||||
{
|
||||
if (g is not MeshGeometryModel3D geometryModel)
|
||||
continue;
|
||||
|
||||
geometryModel.RenderWireframe = !geometryModel.RenderWireframe;
|
||||
}
|
||||
}
|
||||
|
||||
public void DiffuseOnlyToggle()
|
||||
{
|
||||
if (SelectedModel == null) return;
|
||||
foreach (var g in SelectedModel.Group3d)
|
||||
{
|
||||
if (g is not MeshGeometryModel3D { Material: PBRMaterial mat })
|
||||
continue;
|
||||
|
||||
//mat.RenderAmbientOcclusionMap = !mat.RenderAmbientOcclusionMap;
|
||||
mat.RenderDisplacementMap = !mat.RenderDisplacementMap;
|
||||
//mat.RenderEmissiveMap = !mat.RenderEmissiveMap;
|
||||
mat.RenderEnvironmentMap = !mat.RenderEnvironmentMap;
|
||||
mat.RenderIrradianceMap = !mat.RenderIrradianceMap;
|
||||
mat.RenderRoughnessMetallicMap = !mat.RenderRoughnessMetallicMap;
|
||||
mat.RenderShadowMap = !mat.RenderShadowMap;
|
||||
mat.RenderNormalMap = !mat.RenderNormalMap;
|
||||
}
|
||||
}
|
||||
|
||||
public void FocusOnSelectedMesh()
|
||||
{
|
||||
Cam.AnimateTo(SelectedModel.Position, SelectedModel.LookDirection, new Vector3D(0, 1, 0), 500);
|
||||
}
|
||||
|
||||
public void SaveLoadedModels()
|
||||
{
|
||||
if (_loadedModels.Count < 1) return;
|
||||
|
||||
var folderBrowser = new VistaFolderBrowserDialog {ShowNewFolderButton = true};
|
||||
if (folderBrowser.ShowDialog() == false) return;
|
||||
|
||||
foreach (var model in _loadedModels)
|
||||
{
|
||||
var toSave = new CUE4Parse_Conversion.Exporter(model.Export, UserSettings.Default.TextureExportFormat, UserSettings.Default.LodExportFormat, UserSettings.Default.MeshExportFormat);
|
||||
if (toSave.TryWriteToDir(new DirectoryInfo(folderBrowser.SelectedPath), out var savedFileName))
|
||||
{
|
||||
Log.Information("Successfully saved {FileName}", savedFileName);
|
||||
FLogger.AppendInformation();
|
||||
FLogger.AppendText($"Successfully saved {savedFileName}", Constants.WHITE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("{FileName} could not be saved", savedFileName);
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText($"Could not save '{savedFileName}'", Constants.WHITE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CopySelectedMaterialName()
|
||||
{
|
||||
if (SelectedModel is not { } m || m.SelectedGeometry is null)
|
||||
return;
|
||||
|
||||
Clipboard.SetText(m.SelectedGeometry.Name.TrimEnd());
|
||||
}
|
||||
|
||||
public async Task<bool> TryChangeSelectedMaterial(UMaterialInstance materialInstance)
|
||||
{
|
||||
if (SelectedModel is not { } model || model.SelectedGeometry is null)
|
||||
return false;
|
||||
|
||||
PBRMaterial m = null;
|
||||
await _threadWorkerView.Begin(_ =>
|
||||
{
|
||||
var (material, _, _) = LoadMaterial(materialInstance);
|
||||
m = material;
|
||||
});
|
||||
|
||||
if (m == null) return false;
|
||||
model.SelectedGeometry.Material = m;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool TryLoadMaterialInstance(UMaterialInstance materialInstance, ModelAndCam cam)
|
||||
{
|
||||
var builder = new MeshBuilder();
|
||||
builder.AddSphere(Vector3.Zero, 10);
|
||||
cam.TriangleCount = 1984; // no need to count
|
||||
|
||||
SetupCameraAndAxis(new FBox(new FVector(-11), new FVector(11)), cam);
|
||||
var (m, isRendering, isTransparent) = LoadMaterial(materialInstance);
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
cam.Group3d.Add(new MeshGeometryModel3D
|
||||
{
|
||||
Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1,0,0), -90)),
|
||||
Name = FixName(materialInstance.Name), Geometry = builder.ToMeshGeometry3D(),
|
||||
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLoadStaticMesh(UStaticMesh mesh, ModelAndCam cam)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox, cam);
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, cam);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLoadSkeletalMesh(USkeletalMesh mesh, ModelAndCam cam)
|
||||
{
|
||||
if (!mesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupCameraAndAxis(convertedMesh.BoundingBox, cam);
|
||||
foreach (var lod in convertedMesh.LODs)
|
||||
{
|
||||
if (lod.SkipLod) continue;
|
||||
PushLod(lod.Sections.Value, lod.Verts, lod.Indices.Value, cam);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PushLod(CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, ModelAndCam cam)
|
||||
{
|
||||
foreach (var section in sections) // each section is a mesh part with its own material
|
||||
{
|
||||
var builder = new MeshBuilder();
|
||||
cam.TriangleCount += section.NumFaces; // NumFaces * 3 (triangle) = next section FirstIndex
|
||||
for (var j = 0; j < section.NumFaces; j++) // draw a triangle for each face
|
||||
{
|
||||
foreach (var t in _facesIndex) // triangle face 1 then 0 then 2
|
||||
{
|
||||
var id = section.FirstIndex + j * 3 + t;
|
||||
var vert = verts[indices[id]];
|
||||
var p = new Vector3(vert.Position.X, vert.Position.Z, vert.Position.Y); // up direction is Y
|
||||
var n = new Vector3(vert.Normal.X, vert.Normal.Z, vert.Normal.Y);
|
||||
n.Normalize();
|
||||
var uv = new Vector2(vert.UV.U, vert.UV.V);
|
||||
builder.AddNode(p, n, uv);
|
||||
builder.TriangleIndices.Add(j * 3 + t); // one mesh part is "j * 3 + t" use "id" if you're building the full mesh
|
||||
}
|
||||
}
|
||||
|
||||
if (section.Material == null || !section.Material.TryLoad<UMaterialInterface>(out var unrealMaterial))
|
||||
continue;
|
||||
|
||||
var (m, isRendering, isTransparent) = LoadMaterial(unrealMaterial);
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
cam.Group3d.Add(new MeshGeometryModel3D
|
||||
{
|
||||
Name = FixName(unrealMaterial.Name), Geometry = builder.ToMeshGeometry3D(),
|
||||
Material = m, IsTransparent = isTransparent, IsRendering = isRendering
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private (PBRMaterial material, bool isRendering, bool isTransparent) LoadMaterial(UMaterialInterface unrealMaterial)
|
||||
{
|
||||
var m = new PBRMaterial { RenderShadowMap = true, EnableAutoTangent = true, RenderEnvironmentMap = true };
|
||||
var parameters = new CMaterialParams();
|
||||
unrealMaterial.GetParams(parameters);
|
||||
|
||||
var isRendering = !parameters.IsNull;
|
||||
if (isRendering)
|
||||
{
|
||||
if (parameters.Diffuse is UTexture2D diffuse)
|
||||
m.AlbedoMap = new TextureModel(diffuse.Decode()?.Encode().AsStream());
|
||||
if (parameters.Normal is UTexture2D normal)
|
||||
m.NormalMap = new TextureModel(normal.Decode()?.Encode().AsStream());
|
||||
if (parameters.Specular is UTexture2D specular)
|
||||
{
|
||||
var mip = specular.GetFirstMip();
|
||||
TextureDecoder.DecodeTexture(mip, specular.Format, specular.isNormalMap,
|
||||
out var data, out var colorType);
|
||||
|
||||
switch (_game)
|
||||
{
|
||||
case FGame.FortniteGame:
|
||||
{
|
||||
// Fortnite's Specular Texture Channels
|
||||
// R Specular
|
||||
// G Metallic
|
||||
// B Roughness
|
||||
unsafe
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
d[offset] = 0;
|
||||
(d[offset+1], d[offset+2]) = (d[offset+2], d[offset+1]); // swap G and B
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
parameters.RoughnessValue = 1;
|
||||
parameters.MetallicValue = 1;
|
||||
break;
|
||||
}
|
||||
case FGame.ShooterGame:
|
||||
{
|
||||
// Valorant's Specular Texture Channels
|
||||
// R Metallic
|
||||
// G Specular
|
||||
// B Roughness
|
||||
unsafe
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
(d[offset], d[offset+2]) = (d[offset+2], d[offset]); // swap R and B
|
||||
(d[offset], d[offset+1]) = (d[offset+1], d[offset]); // swap B and G
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
parameters.RoughnessValue = 1;
|
||||
parameters.MetallicValue = 1;
|
||||
break;
|
||||
}
|
||||
case FGame.Gameface:
|
||||
{
|
||||
// GTA's Specular Texture Channels
|
||||
// R Metallic
|
||||
// G Roughness
|
||||
// B Specular
|
||||
unsafe
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
(d[offset], d[offset+2]) = (d[offset+2], d[offset]); // swap R and B
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using var bitmap = new SKBitmap(new SKImageInfo(mip.SizeX, mip.SizeY, colorType, SKAlphaType.Unpremul));
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = data)
|
||||
{
|
||||
bitmap.SetPixels(new IntPtr(p));
|
||||
}
|
||||
}
|
||||
|
||||
// R -> AO G -> Roughness B -> Metallic
|
||||
m.RoughnessMetallicMap = new TextureModel(bitmap.Encode(SKEncodedImageFormat.Png, 100).AsStream());
|
||||
m.RoughnessFactor = parameters.RoughnessValue;
|
||||
m.MetallicFactor = parameters.MetallicValue;
|
||||
m.RenderAmbientOcclusionMap = parameters.SpecularValue > 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m.AlbedoColor = new Color4(1, 0, 0, 1);
|
||||
}
|
||||
|
||||
return (m, isRendering, parameters.IsTransparent);
|
||||
}
|
||||
|
||||
private void SetupCameraAndAxis(FBox box, ModelAndCam cam)
|
||||
{
|
||||
if (AppendMode && CanAppend) return;
|
||||
var center = box.GetCenter();
|
||||
|
||||
var lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(0, 0, 0), new Vector3(max.X, 0, 0));
|
||||
XAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder.AddLine(new Vector3(box.Min.X, center.Z, center.Y), new Vector3(box.Max.X, center.Z, center.Y));
|
||||
cam.XAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(0, 0, 0), new Vector3(0, max.Y, 0));
|
||||
YAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder.AddLine(new Vector3(center.X, box.Min.Z, center.Y), new Vector3(center.X, box.Max.Z, center.Y));
|
||||
cam.YAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder = new LineBuilder();
|
||||
lineBuilder.AddLine(new Vector3(0, 0, 0), new Vector3(0, 0, max.Z));
|
||||
ZAxis = lineBuilder.ToLineGeometry3D();
|
||||
lineBuilder.AddLine(new Vector3(center.X, center.Z, box.Min.Y), new Vector3(center.X, center.Z, box.Max.Y));
|
||||
cam.ZAxis = lineBuilder.ToLineGeometry3D();
|
||||
|
||||
cam.Position = new Point3D(box.Max.X + center.X * 2, center.Z, box.Min.Y + center.Y * 2);
|
||||
cam.LookDirection = new Vector3D(-cam.Position.X + center.X, 0, -cam.Position.Z + center.Y);
|
||||
}
|
||||
|
||||
private string FixName(string input)
|
||||
{
|
||||
if (input.Length < 1)
|
||||
return "Material_Has_No_Name";
|
||||
|
||||
if (int.TryParse(input[0].ToString(), out _))
|
||||
input = input[1..];
|
||||
|
||||
return input.Replace('-', '_');
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var g in _loadedModels.ToList())
|
||||
{
|
||||
g.Dispose();
|
||||
_loadedModels.Remove(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ModelAndCam : ViewModel
|
||||
{
|
||||
public UObject Export { get; }
|
||||
public Point3D Position { get; set; }
|
||||
public Vector3D LookDirection { get; set; }
|
||||
public Geometry3D XAxis { get; set; }
|
||||
public Geometry3D YAxis { get; set; }
|
||||
public Geometry3D ZAxis { get; set; }
|
||||
public int TriangleCount { get; set; }
|
||||
|
||||
private MeshGeometryModel3D _selectedGeometry; // selected material
|
||||
public MeshGeometryModel3D SelectedGeometry
|
||||
{
|
||||
get => _selectedGeometry;
|
||||
set => SetProperty(ref _selectedGeometry, value);
|
||||
}
|
||||
|
||||
private ObservableElement3DCollection _group3d; // material list
|
||||
public ObservableElement3DCollection Group3d
|
||||
{
|
||||
get => _group3d;
|
||||
set => SetProperty(ref _group3d, value);
|
||||
}
|
||||
|
||||
public ModelAndCam(UObject export)
|
||||
{
|
||||
Export = export;
|
||||
TriangleCount = 0;
|
||||
Group3d = new ObservableElement3DCollection();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TriangleCount = 0;
|
||||
SelectedGeometry = null;
|
||||
foreach (var g in Group3d.ToList())
|
||||
{
|
||||
g.Dispose();
|
||||
Group3d.Remove(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,14 +53,14 @@ namespace FModel.ViewModels
|
|||
get => _selectedUeVersion;
|
||||
set => SetProperty(ref _selectedUeVersion, value);
|
||||
}
|
||||
|
||||
|
||||
private List<FCustomVersion> _selectedCustomVersions;
|
||||
public List<FCustomVersion> SelectedCustomVersions
|
||||
{
|
||||
get => _selectedCustomVersions;
|
||||
set => SetProperty(ref _selectedCustomVersions, value);
|
||||
}
|
||||
|
||||
|
||||
private Dictionary<string, bool> _selectedOptions;
|
||||
public Dictionary<string, bool> SelectedOptions
|
||||
{
|
||||
|
|
@ -89,13 +89,6 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _selectedDiscordRpc, value);
|
||||
}
|
||||
|
||||
private EEnabledDisabled _selectedDirectoryStructure;
|
||||
public EEnabledDisabled SelectedDirectoryStructure
|
||||
{
|
||||
get => _selectedDirectoryStructure;
|
||||
set => SetProperty(ref _selectedDirectoryStructure, value);
|
||||
}
|
||||
|
||||
private ECompressedAudio _selectedCompressedAudio;
|
||||
public ECompressedAudio SelectedCompressedAudio
|
||||
{
|
||||
|
|
@ -110,27 +103,20 @@ namespace FModel.ViewModels
|
|||
set => SetProperty(ref _selectedCosmeticStyle, value);
|
||||
}
|
||||
|
||||
private EEnabledDisabled _selectedCosmeticDisplayAsset;
|
||||
public EEnabledDisabled SelectedCosmeticDisplayAsset
|
||||
{
|
||||
get => _selectedCosmeticDisplayAsset;
|
||||
set => SetProperty(ref _selectedCosmeticDisplayAsset, value);
|
||||
}
|
||||
|
||||
private EMeshFormat _selectedMeshExportFormat;
|
||||
public EMeshFormat SelectedMeshExportFormat
|
||||
{
|
||||
get => _selectedMeshExportFormat;
|
||||
set => SetProperty(ref _selectedMeshExportFormat, value);
|
||||
}
|
||||
|
||||
|
||||
private ELodFormat _selectedLodExportFormat;
|
||||
public ELodFormat SelectedLodExportFormat
|
||||
{
|
||||
get => _selectedLodExportFormat;
|
||||
set => SetProperty(ref _selectedLodExportFormat, value);
|
||||
}
|
||||
|
||||
|
||||
private ETextureFormat _selectedTextureExportFormat;
|
||||
public ETextureFormat SelectedTextureExportFormat
|
||||
{
|
||||
|
|
@ -145,10 +131,8 @@ namespace FModel.ViewModels
|
|||
public ReadOnlyObservableCollection<ELanguage> AssetLanguages { get; private set; }
|
||||
public ReadOnlyObservableCollection<EAesReload> AesReloads { get; private set; }
|
||||
public ReadOnlyObservableCollection<EDiscordRpc> DiscordRpcs { get; private set; }
|
||||
public ReadOnlyObservableCollection<EEnabledDisabled> DirectoryStructures { get; private set; }
|
||||
public ReadOnlyObservableCollection<ECompressedAudio> CompressedAudios { get; private set; }
|
||||
public ReadOnlyObservableCollection<EIconStyle> CosmeticStyles { get; private set; }
|
||||
public ReadOnlyObservableCollection<EEnabledDisabled> CosmeticDisplayAssets { get; private set; }
|
||||
public ReadOnlyObservableCollection<EMeshFormat> MeshExportFormats { get; private set; }
|
||||
public ReadOnlyObservableCollection<ELodFormat> LodExportFormats { get; private set; }
|
||||
public ReadOnlyObservableCollection<ETextureFormat> TextureExportFormats { get; private set; }
|
||||
|
|
@ -166,10 +150,8 @@ namespace FModel.ViewModels
|
|||
private List<FCustomVersion> _customVersionsSnapshot;
|
||||
private Dictionary<string, bool> _optionsSnapshot;
|
||||
private ELanguage _assetLanguageSnapshot;
|
||||
private EEnabledDisabled _directoryStructureSnapshot;
|
||||
private ECompressedAudio _compressedAudioSnapshot;
|
||||
private EIconStyle _cosmeticStyleSnapshot;
|
||||
private EEnabledDisabled _cosmeticDisplayAssetSnapshot;
|
||||
private EMeshFormat _meshExportFormatSnapshot;
|
||||
private ELodFormat _lodExportFormatSnapshot;
|
||||
private ETextureFormat _textureExportFormatSnapshot;
|
||||
|
|
@ -190,10 +172,8 @@ namespace FModel.ViewModels
|
|||
_customVersionsSnapshot = UserSettings.Default.OverridedCustomVersions[_game];
|
||||
_optionsSnapshot = UserSettings.Default.OverridedOptions[_game];
|
||||
_assetLanguageSnapshot = UserSettings.Default.AssetLanguage;
|
||||
_directoryStructureSnapshot = UserSettings.Default.KeepDirectoryStructure;
|
||||
_compressedAudioSnapshot = UserSettings.Default.CompressedAudioMode;
|
||||
_cosmeticStyleSnapshot = UserSettings.Default.CosmeticStyle;
|
||||
_cosmeticDisplayAssetSnapshot = UserSettings.Default.CosmeticDisplayAsset;
|
||||
_meshExportFormatSnapshot = UserSettings.Default.MeshExportFormat;
|
||||
_lodExportFormatSnapshot = UserSettings.Default.LodExportFormat;
|
||||
_textureExportFormatSnapshot = UserSettings.Default.TextureExportFormat;
|
||||
|
|
@ -205,10 +185,8 @@ namespace FModel.ViewModels
|
|||
SelectedCustomVersions = _customVersionsSnapshot;
|
||||
SelectedOptions = _optionsSnapshot;
|
||||
SelectedAssetLanguage = _assetLanguageSnapshot;
|
||||
SelectedDirectoryStructure = _directoryStructureSnapshot;
|
||||
SelectedCompressedAudio = _compressedAudioSnapshot;
|
||||
SelectedCosmeticStyle = _cosmeticStyleSnapshot;
|
||||
SelectedCosmeticDisplayAsset = _cosmeticDisplayAssetSnapshot;
|
||||
SelectedMeshExportFormat = _meshExportFormatSnapshot;
|
||||
SelectedLodExportFormat = _lodExportFormatSnapshot;
|
||||
SelectedTextureExportFormat = _textureExportFormatSnapshot;
|
||||
|
|
@ -222,10 +200,8 @@ namespace FModel.ViewModels
|
|||
AssetLanguages = new ReadOnlyObservableCollection<ELanguage>(new ObservableCollection<ELanguage>(EnumerateAssetLanguages()));
|
||||
AesReloads = new ReadOnlyObservableCollection<EAesReload>(new ObservableCollection<EAesReload>(EnumerateAesReloads()));
|
||||
DiscordRpcs = new ReadOnlyObservableCollection<EDiscordRpc>(new ObservableCollection<EDiscordRpc>(EnumerateDiscordRpcs()));
|
||||
DirectoryStructures = new ReadOnlyObservableCollection<EEnabledDisabled>(new ObservableCollection<EEnabledDisabled>(EnumerateEnabledDisabled()));
|
||||
CompressedAudios = new ReadOnlyObservableCollection<ECompressedAudio>(new ObservableCollection<ECompressedAudio>(EnumerateCompressedAudios()));
|
||||
CosmeticStyles = new ReadOnlyObservableCollection<EIconStyle>(new ObservableCollection<EIconStyle>(EnumerateCosmeticStyles()));
|
||||
CosmeticDisplayAssets = new ReadOnlyObservableCollection<EEnabledDisabled>(new ObservableCollection<EEnabledDisabled>(EnumerateEnabledDisabled()));
|
||||
MeshExportFormats = new ReadOnlyObservableCollection<EMeshFormat>(new ObservableCollection<EMeshFormat>(EnumerateMeshExportFormat()));
|
||||
LodExportFormats = new ReadOnlyObservableCollection<ELodFormat>(new ObservableCollection<ELodFormat>(EnumerateLodExportFormat()));
|
||||
TextureExportFormats = new ReadOnlyObservableCollection<ETextureFormat>(new ObservableCollection<ETextureFormat>(EnumerateTextureExportFormat()));
|
||||
|
|
@ -238,7 +214,7 @@ namespace FModel.ViewModels
|
|||
if (string.IsNullOrEmpty(gameName)) return;
|
||||
_gamePreset = _apiEndpointView.FModelApi.GetGames(cancellationToken, gameName);
|
||||
});
|
||||
|
||||
|
||||
if (_gamePreset?.Versions == null) return;
|
||||
foreach (var version in _gamePreset.Versions.Keys)
|
||||
{
|
||||
|
|
@ -251,7 +227,7 @@ namespace FModel.ViewModels
|
|||
if (_gamePreset?.Versions == null || !_gamePreset.Versions.TryGetValue(key, out var version)) return;
|
||||
SelectedUeGame = version.GameEnum.ToEnum(EGame.GAME_UE4_LATEST);
|
||||
SelectedUeVersion = (UE4Version)version.UeVer;
|
||||
|
||||
|
||||
SelectedCustomVersions = new List<FCustomVersion>();
|
||||
foreach (var (guid, v) in version.CustomVersions)
|
||||
{
|
||||
|
|
@ -264,7 +240,7 @@ namespace FModel.ViewModels
|
|||
SelectedOptions[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ResetPreset()
|
||||
{
|
||||
SelectedUeGame = _ueGameSnapshot;
|
||||
|
|
@ -293,10 +269,8 @@ namespace FModel.ViewModels
|
|||
UserSettings.Default.OverridedCustomVersions[_game] = SelectedCustomVersions;
|
||||
UserSettings.Default.OverridedOptions[_game] = SelectedOptions;
|
||||
UserSettings.Default.AssetLanguage = SelectedAssetLanguage;
|
||||
UserSettings.Default.KeepDirectoryStructure = SelectedDirectoryStructure;
|
||||
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;
|
||||
UserSettings.Default.CosmeticStyle = SelectedCosmeticStyle;
|
||||
UserSettings.Default.CosmeticDisplayAsset = SelectedCosmeticDisplayAsset;
|
||||
UserSettings.Default.MeshExportFormat = SelectedMeshExportFormat;
|
||||
UserSettings.Default.LodExportFormat = SelectedLodExportFormat;
|
||||
UserSettings.Default.TextureExportFormat = SelectedTextureExportFormat;
|
||||
|
|
@ -321,9 +295,8 @@ namespace FModel.ViewModels
|
|||
private IEnumerable<EDiscordRpc> EnumerateDiscordRpcs() => Enum.GetValues(SelectedDiscordRpc.GetType()).Cast<EDiscordRpc>();
|
||||
private IEnumerable<ECompressedAudio> EnumerateCompressedAudios() => Enum.GetValues(SelectedCompressedAudio.GetType()).Cast<ECompressedAudio>();
|
||||
private IEnumerable<EIconStyle> EnumerateCosmeticStyles() => Enum.GetValues(SelectedCosmeticStyle.GetType()).Cast<EIconStyle>();
|
||||
private IEnumerable<EEnabledDisabled> EnumerateEnabledDisabled() => Enum.GetValues(SelectedCosmeticDisplayAsset.GetType()).Cast<EEnabledDisabled>();
|
||||
private IEnumerable<EMeshFormat> EnumerateMeshExportFormat() => Enum.GetValues(SelectedMeshExportFormat.GetType()).Cast<EMeshFormat>();
|
||||
private IEnumerable<ELodFormat> EnumerateLodExportFormat() => Enum.GetValues(SelectedLodExportFormat.GetType()).Cast<ELodFormat>();
|
||||
private IEnumerable<ETextureFormat> EnumerateTextureExportFormat() => Enum.GetValues(SelectedTextureExportFormat.GetType()).Cast<ETextureFormat>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using FModel.Extensions;
|
||||
using System;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.Commands;
|
||||
|
|
@ -119,7 +120,7 @@ namespace FModel.ViewModels
|
|||
SetProperty(ref _highlighter, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] ImageBuffer { get; private set; }
|
||||
|
||||
private BitmapImage _image;
|
||||
|
|
@ -133,7 +134,7 @@ namespace FModel.ViewModels
|
|||
RaisePropertyChanged("HasImage");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool _noAlpha;
|
||||
public bool NoAlpha
|
||||
{
|
||||
|
|
@ -193,7 +194,7 @@ namespace FModel.ViewModels
|
|||
{
|
||||
var fileName = Path.ChangeExtension(Header, ".json");
|
||||
var directory = Path.Combine(UserSettings.Default.OutputDirectory, "Saves",
|
||||
UserSettings.Default.KeepDirectoryStructure == EEnabledDisabled.Enabled ? Directory : "", fileName).Replace('\\', '/');
|
||||
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName).Replace('\\', '/');
|
||||
|
||||
if (!autoSave)
|
||||
{
|
||||
|
|
@ -222,7 +223,7 @@ namespace FModel.ViewModels
|
|||
public void SetImage(SKImage img)
|
||||
{
|
||||
_img = img;
|
||||
|
||||
|
||||
using var data = _img.Encode(NoAlpha ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100);
|
||||
using var stream = new MemoryStream(ImageBuffer = data.ToArray(), false);
|
||||
var image = new BitmapImage();
|
||||
|
|
@ -242,7 +243,7 @@ namespace FModel.ViewModels
|
|||
if (!HasImage) return;
|
||||
var fileName = Path.ChangeExtension(Header, ".png");
|
||||
var directory = Path.Combine(UserSettings.Default.OutputDirectory, "Textures",
|
||||
UserSettings.Default.KeepDirectoryStructure == EEnabledDisabled.Enabled ? Directory : "", fileName!).Replace('\\', '/');
|
||||
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName!).Replace('\\', '/');
|
||||
|
||||
if (!autoSave)
|
||||
{
|
||||
|
|
@ -315,9 +316,19 @@ namespace FModel.ViewModels
|
|||
public void AddTab(string header = null, string directory = null)
|
||||
{
|
||||
if (!CanAddTabs) return;
|
||||
|
||||
var h = header ?? "New Tab";
|
||||
var d = directory ?? string.Empty;
|
||||
if (SelectedTab is { Header : "New Tab" })
|
||||
{
|
||||
SelectedTab.Header = h;
|
||||
SelectedTab.Directory = d;
|
||||
return;
|
||||
}
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
_tabItems.Add(new TabItem(header ?? "New Tab", directory ?? string.Empty));
|
||||
_tabItems.Add(new TabItem(h, d));
|
||||
SelectedTab = _tabItems.Last();
|
||||
});
|
||||
}
|
||||
|
|
@ -339,9 +350,16 @@ namespace FModel.ViewModels
|
|||
}
|
||||
|
||||
_tabItems.Remove(tabToDelete);
|
||||
OnTabRemove?.Invoke(this, new TabEventArgs(tabToDelete));
|
||||
});
|
||||
}
|
||||
|
||||
public class TabEventArgs : EventArgs
|
||||
{
|
||||
public TabItem TabToRemove { get; set; }
|
||||
public TabEventArgs(TabItem tab) { TabToRemove = tab; }
|
||||
}
|
||||
public event EventHandler OnTabRemove;
|
||||
public void GoLeftTab() => SelectedTab = _tabItems.Previous(SelectedTab);
|
||||
public void GoRightTab() => SelectedTab = _tabItems.Next(SelectedTab);
|
||||
|
||||
|
|
@ -370,4 +388,4 @@ namespace FModel.ViewModels
|
|||
yield return new TabItem("New Tab", string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ namespace FModel.ViewModels
|
|||
CurrentCancellationTokenSource = null; // kill token
|
||||
|
||||
Log.Error("{Exception}", e);
|
||||
|
||||
|
||||
FLogger.AppendError();
|
||||
FLogger.AppendText(e.Message, Constants.WHITE, true);
|
||||
FLogger.AppendText(" " + e.StackTrace.SubstringBefore('\n').Trim(), Constants.WHITE, true);
|
||||
|
|
@ -116,4 +116,4 @@ namespace FModel.ViewModels
|
|||
StatusChangeAttempted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
|
||||
<controls:OnTagDataTemplateSelector x:Key="TagTemplateSelector" />
|
||||
<DataTemplate x:Key="BrTemplate">
|
||||
<StackPanel VerticalAlignment="Center" Margin="25 0">
|
||||
|
|
@ -39,9 +39,6 @@
|
|||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Fireflies" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrFireflies}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
<CheckBox Content="Corruption Zones" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrCorruptionZones}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}"
|
||||
ToolTip="Saving the image with Corruption Zones enabled will smooth these ugly sharp edges"/>
|
||||
<CheckBox Content="Cube Movements" Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" IsChecked="{Binding MapViewer.BrCubeMovements}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.MapViewer}}}" IsEnabled="{Binding IsReady}" />
|
||||
</StackPanel>
|
||||
|
|
@ -73,14 +70,14 @@
|
|||
<ColumnDefinition Width="Auto" MinWidth="300" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<TreeView Grid.Row="0" Grid.RowSpan="3" x:Name="MapTree" SelectedItemChanged="OnSelectedItemChanged"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}">
|
||||
<TreeViewItem Tag="BrTemplate" IsSelected="True">
|
||||
|
|
@ -108,14 +105,14 @@
|
|||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
</TreeView>
|
||||
|
||||
|
||||
<Grid Grid.Row="1" HorizontalAlignment="Stretch">
|
||||
<ContentControl ContentTemplateSelector="{StaticResource TagTemplateSelector}" Content="{Binding SelectedItem.Tag, ElementName=MapTree}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
<Button Grid.Row="2" Content="Save Image" Margin="5" IsEnabled="{Binding IsReady}" VerticalAlignment="Bottom" Click="OnClick" />
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid Grid.Column="1" HorizontalAlignment="Stretch">
|
||||
<controls:MagnifierManager.Magnifier>
|
||||
<controls:Magnifier Radius="200" ZoomFactor=".4" BorderBrush="{DynamicResource {x:Static adonisUi:Brushes.AccentBrush}}" BorderThickness="1" />
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
<adonisControls:AdonisWindow x:Class="FModel.Views.ModelViewer"
|
||||
<adonisControls:AdonisWindow x:Class="FModel.Views.ModelViewer"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||
xmlns:helix="http://helix-toolkit.org/wpf/SharpDX"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanResize" IconVisibility="Collapsed" Background="#262630"
|
||||
PreviewKeyDown="OnWindowKeyDown"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanResize" IconVisibility="Collapsed"
|
||||
PreviewKeyDown="OnWindowKeyDown" Closing="OnClosing"
|
||||
Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.60'}"
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}">
|
||||
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.60'}">
|
||||
<adonisControls:AdonisWindow.Style>
|
||||
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
|
||||
<Setter Property="Title" Value="Model Viewer" />
|
||||
|
|
@ -21,27 +22,118 @@
|
|||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
<helix:Viewport3DX EffectsManager="{Binding ModelViewer.EffectManager}" Camera="{Binding ModelViewer.Cam}"
|
||||
IsChangeFieldOfViewEnabled="False" IsMoveEnabled="False" UseDefaultGestures="False"
|
||||
ShowViewCube="False" ShowCameraTarget="False" ModelUpDirection="0,0,1"
|
||||
EnableSSAO="True" MSAA="Maximum" FXAALevel="Ultra" SSAOQuality="High"
|
||||
BackgroundColor="#262630">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="350" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="4*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<GroupBox Grid.Column="0" Padding="{adonisUi:Space 0}" Background="Transparent">
|
||||
<DockPanel Margin="10">
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Models" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding ModelViewer.LoadedModelsView, IsAsync=True}"
|
||||
SelectedItem="{Binding ModelViewer.SelectedModel, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Export.Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Triangles" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding ModelViewer.SelectedModel.TriangleCount, FallbackValue=0, StringFormat={}{0:### ### ### ###}}" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Materials" VerticalAlignment="Center" Margin="0 0 0 10" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="2" Text="{Binding ModelViewer.SelectedModel.Group3d.Count, FallbackValue=0, StringFormat={}{0:### ###}}" VerticalAlignment="Center" HorizontalAlignment="Right" />
|
||||
|
||||
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button Grid.Row="0" Grid.Column="0" Content="Focus" Click="OnFocusClick" />
|
||||
<ToggleButton Grid.Row="0" Grid.Column="2" IsChecked="{Binding ModelViewer.AppendMode}" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}">
|
||||
<TextBlock Text="{Binding IsChecked, Converter={x:Static converters:BoolToToggleConverter.Instance},
|
||||
StringFormat={}Append {0}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ToggleButton}}}" />
|
||||
</ToggleButton>
|
||||
|
||||
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Click="Save"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.AccentButton}}">
|
||||
<TextBlock Text="{Binding ModelViewer.LoadedModelsView.Count, StringFormat={}Save All Loaded Models ({0})}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Separator DockPanel.Dock="Top" Tag="MATERIALS" Style="{StaticResource CustomSeparator}" />
|
||||
<ListBox DockPanel.Dock="Top" Style="{StaticResource MaterialsListBox}">
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Copy Name" Click="OnCopyClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource CopyIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Change Material" Click="OnChangeMaterialClick">
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource SwapIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</ListBox.ContextMenu>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GridSplitter Grid.Column="1" ResizeDirection="Columns" Width="4" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}" />
|
||||
|
||||
<helix:Viewport3DX Grid.Column="2" EffectsManager="{Binding ModelViewer.EffectManager}" Camera="{Binding ModelViewer.Cam}"
|
||||
IsChangeFieldOfViewEnabled="False" IsMoveEnabled="False" UseDefaultGestures="False" ShowViewCube="False"
|
||||
ShowCameraTarget="False" FXAALevel="Ultra" MSAA="Maximum" BackgroundColor="#2A2B34"
|
||||
EnableSSAO="True" SSAOIntensity="1" EnableSwapChainRendering="True">
|
||||
<helix:Viewport3DX.InputBindings>
|
||||
<MouseBinding Command="helix:ViewportCommands.Rotate" Gesture="LeftClick" />
|
||||
<MouseBinding Command="helix:ViewportCommands.Zoom" Gesture="RightClick" />
|
||||
<MouseBinding Command="helix:ViewportCommands.Pan" Gesture="MiddleClick" />
|
||||
<KeyBinding Command="helix:ViewportCommands.ZoomExtents" Modifiers="Shift" Key="C"/>
|
||||
</helix:Viewport3DX.InputBindings>
|
||||
|
||||
<helix:DirectionalLight3D Direction="0, 0, -1" Color="White" />
|
||||
<helix:DirectionalLight3D Direction="0, -1, 0" Color="White"/>
|
||||
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.XAxis}" Color="#FC3854" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.YAxis}" Color="#85CB22" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.ZAxis}" Color="#388EED" />
|
||||
|
||||
<helix:MeshGeometryModel3D Geometry="{Binding ModelViewer.Mesh}"
|
||||
Material="{Binding ModelViewer.MeshMat}"
|
||||
RenderWireframe="{Binding ModelViewer.ShowWireframe}" />
|
||||
|
||||
<helix:EnvironmentMap3D Texture="{Binding ModelViewer.HDRi}" />
|
||||
<helix:DirectionalLight3D Color="White" Direction="{Binding Camera.LookDirection,
|
||||
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type helix:Viewport3DX}}}" />
|
||||
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.SelectedModel.XAxis}" Color="#FC3854" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.SelectedModel.YAxis}" Color="#85CB22" />
|
||||
<helix:LineGeometryModel3D Geometry="{Binding ModelViewer.SelectedModel.ZAxis}" Color="#388EED" />
|
||||
|
||||
<helix:GroupModel3D x:Name="MyAntiCrashGroup" ItemsSource="{Binding ModelViewer.SelectedModel.Group3d}" />
|
||||
</helix:Viewport3DX>
|
||||
</Grid>
|
||||
</adonisControls:AdonisWindow>
|
||||
|
|
|
|||
|
|
@ -1,31 +1,101 @@
|
|||
using System.Windows.Input;
|
||||
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.Settings;
|
||||
using FModel.ViewModels;
|
||||
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 async void Load(UObject export) => await _applicationView.ModelViewer.LoadExport(export);
|
||||
public async void Swap(UMaterialInstance materialInstance)
|
||||
{
|
||||
var sucess = await _applicationView.ModelViewer.TryChangeSelectedMaterial(materialInstance);
|
||||
if (sucess)
|
||||
{
|
||||
_applicationView.CUE4Parse.ModelIsSwappingMaterial = 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.ModelIsSwappingMaterial = false;
|
||||
MyAntiCrashGroup.ItemsSource = null; // <3
|
||||
}
|
||||
|
||||
private void OnWindowKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (UserSettings.Default.DirLeftTab.IsTriggered(e.Key))
|
||||
_applicationView.ModelViewer.PreviousLod();
|
||||
else if (UserSettings.Default.DirRightTab.IsTriggered(e.Key))
|
||||
_applicationView.ModelViewer.NextLod();
|
||||
else if (e.Key == Key.W)
|
||||
_applicationView.ModelViewer.ShowWireframe = !_applicationView.ModelViewer.ShowWireframe;
|
||||
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.Decimal:
|
||||
_applicationView.ModelViewer.FocusOnSelectedMesh();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFocusClick(object sender, RoutedEventArgs e)
|
||||
=> _applicationView.ModelViewer.FocusOnSelectedMesh();
|
||||
|
||||
private void OnCopyClick(object sender, RoutedEventArgs e)
|
||||
=> _applicationView.ModelViewer.CopySelectedMaterialName();
|
||||
|
||||
private void Save(object sender, RoutedEventArgs e)
|
||||
=> _applicationView.ModelViewer.SaveLoadedModels();
|
||||
|
||||
private void OnChangeMaterialClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_applicationView.CUE4Parse.ModelIsSwappingMaterial = 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 Change Material?",
|
||||
Icon = MessageBoxImage.Information,
|
||||
IsSoundEnabled = false
|
||||
});
|
||||
_messageShown = true;
|
||||
}
|
||||
|
||||
MainWindow.YesWeCats.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<Grid Grid.Row="0" Margin="3 0" HorizontalAlignment="Right" Width="500">
|
||||
<Grid.Style>
|
||||
<Style TargetType="{x:Type Grid}">
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Grid Grid.Column="0" ZIndex="1" HorizontalAlignment="Left" Margin="5 2 0 0">
|
||||
<Viewbox Width="16" Height="16">
|
||||
<Canvas Width="24" Height="24">
|
||||
|
|
@ -113,6 +113,6 @@
|
|||
<avalonEdit:TextEditor x:Name="MyAvalonEditor" Grid.Row="0" Grid.RowSpan="2" SyntaxHighlighting="{Binding Highlighter}" Document="{Binding Document}"
|
||||
FontFamily="Consolas" FontSize="{Binding FontSize}" IsReadOnly="True" ShowLineNumbers="True" Foreground="#DAE5F2"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}" PreviewMouseWheel="OnPreviewMouseWheel"
|
||||
TextChanged="OnTextChanged" MouseHover="OnMouseHover" MouseHoverStopped="OnMouseHoverStopped" />
|
||||
TextChanged="OnTextChanged" MouseHover="OnMouseHover" MouseHoverStopped="OnMouseHoverStopped" PreviewMouseUp="OnMouseRelease" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using CUE4Parse.Utils;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.ViewModels;
|
||||
using ICSharpCode.AvalonEdit;
|
||||
using SkiaSharp;
|
||||
|
|
@ -20,16 +24,30 @@ namespace FModel.Views.Resources.Controls
|
|||
private readonly Regex _hexColorRegex = new("\"Hex\": \"(?'target'[0-9A-Fa-f]{3,8})\"$",
|
||||
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
private readonly System.Windows.Controls.ToolTip _toolTip = new();
|
||||
private readonly Dictionary<string, NavigationList<int>> _savedCarets = new();
|
||||
private NavigationList<int> _caretsOffsets
|
||||
{
|
||||
get
|
||||
{
|
||||
if (MyAvalonEditor.Document != null)
|
||||
return _savedCarets.GetOrAdd(MyAvalonEditor.Document.FileName, () => new NavigationList<int>());
|
||||
else
|
||||
return new NavigationList<int>();
|
||||
}
|
||||
}
|
||||
private bool _ignoreCaret = true;
|
||||
|
||||
public AvalonEditor()
|
||||
{
|
||||
CommandBindings.Add(new CommandBinding(NavigationCommands.Search, (_, e) => FindNext(e.Parameter != null)));
|
||||
InitializeComponent();
|
||||
|
||||
|
||||
YesWeEditor = MyAvalonEditor;
|
||||
YesWeSearch = WpfSuckMyDick;
|
||||
MyAvalonEditor.TextArea.TextView.ElementGenerators.Add(new GamePathElementGenerator());
|
||||
MyAvalonEditor.TextArea.TextView.ElementGenerators.Add(new HexColorElementGenerator());
|
||||
|
||||
ApplicationService.ApplicationView.CUE4Parse.TabControl.OnTabRemove += OnTabClose;
|
||||
}
|
||||
|
||||
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
|
|
@ -49,6 +67,19 @@ namespace FModel.Views.Resources.Controls
|
|||
FindNext();
|
||||
dc.SearchUp = old;
|
||||
break;
|
||||
case Key.System: // Alt
|
||||
if (Keyboard.IsKeyDown(Key.Left))
|
||||
{
|
||||
if (_caretsOffsets.Count == 0) return;
|
||||
MyAvalonEditor.CaretOffset = _caretsOffsets.MovePrevious;
|
||||
MyAvalonEditor.TextArea.Caret.BringCaretToView();
|
||||
} else if ((Keyboard.IsKeyDown(Key.Right)))
|
||||
{
|
||||
if (_caretsOffsets.Count == 0) return;
|
||||
MyAvalonEditor.CaretOffset = _caretsOffsets.MoveNext;
|
||||
MyAvalonEditor.TextArea.Caret.BringCaretToView();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,8 +119,11 @@ namespace FModel.Views.Resources.Controls
|
|||
if (sender is not TextEditor avalonEditor || DataContext is not TabItem tabItem ||
|
||||
avalonEditor.Document == null || string.IsNullOrEmpty(avalonEditor.Document.Text))
|
||||
return;
|
||||
|
||||
avalonEditor.Document.FileName = tabItem.Directory + '/' + tabItem.Header.SubstringBeforeLast('.');
|
||||
avalonEditor.Document.FileName = tabItem.Directory + '/' + StringExtensions.SubstringBeforeLast(tabItem.Header, '.');
|
||||
|
||||
if (!_savedCarets.ContainsKey(avalonEditor.Document.FileName))
|
||||
_ignoreCaret = true;
|
||||
|
||||
if (!tabItem.ShouldScroll) return;
|
||||
|
||||
var lineNumber = avalonEditor.Document.Text.GetLineNumber(tabItem.ScrollTrigger);
|
||||
|
|
@ -102,7 +136,7 @@ namespace FModel.Views.Resources.Controls
|
|||
{
|
||||
if (DataContext is not TabItem tabItem || Keyboard.Modifiers != ModifierKeys.Control)
|
||||
return;
|
||||
|
||||
|
||||
var fontSize = tabItem.FontSize + e.Delta / 50.0;
|
||||
tabItem.FontSize = fontSize switch
|
||||
{
|
||||
|
|
@ -186,5 +220,31 @@ namespace FModel.Views.Resources.Controls
|
|||
{
|
||||
((TabItem) DataContext).HasSearchOpen = false;
|
||||
}
|
||||
|
||||
private void OnTabClose(object sender, EventArgs eventArgs)
|
||||
{
|
||||
if (sender is not TabControlViewModel tab|| eventArgs is not TabControlViewModel.TabEventArgs e)
|
||||
return;
|
||||
var fileName = e.TabToRemove.Document.FileName;
|
||||
if (_savedCarets.ContainsKey(fileName))
|
||||
_savedCarets.Remove(fileName);
|
||||
}
|
||||
|
||||
private void SaveCaretLoc(int offset)
|
||||
{
|
||||
if (_ignoreCaret) { _ignoreCaret = false; return;} // first always point to the end of the file for some reason
|
||||
if (_caretsOffsets.Count >= 10)
|
||||
_caretsOffsets.RemoveAt(0);
|
||||
if (!_caretsOffsets.Contains(offset))
|
||||
{
|
||||
_caretsOffsets.Add(offset);
|
||||
_caretsOffsets.CurrentIndex = _caretsOffsets.Count-1;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseRelease(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
SaveCaretLoc(MyAvalonEditor.CaretOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
FModel/Views/Resources/Converters/BoolToFillModeConverter.cs
Normal file
30
FModel/Views/Resources/Converters/BoolToFillModeConverter.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
25
FModel/Views/Resources/Converters/BoolToToggleConverter.cs
Normal file
25
FModel/Views/Resources/Converters/BoolToToggleConverter.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FModel.Views.Resources.Converters
|
||||
{
|
||||
public class BoolToToggleConverter : IValueConverter
|
||||
{
|
||||
public static readonly BoolToToggleConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
true => "Enabled",
|
||||
_ => "Disabled"
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,8 @@ namespace FModel.Views.Resources.Converters
|
|||
"MinecraftDungeons" => "Minecraft Dungeons",
|
||||
"shoebill" => "Star Wars: Jedi Fallen Order",
|
||||
"a99769d95d8f400baad1f67ab5dfe508" => "Core",
|
||||
578080 => "PLAYERUNKNOWN'S BATTLEGROUNDS",
|
||||
677620 => "Splitgate",
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
|
|
@ -34,4 +36,4 @@ namespace FModel.Views.Resources.Converters
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
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"
|
||||
|
|
@ -14,7 +15,7 @@
|
|||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI">
|
||||
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
|
||||
|
||||
<Geometry x:Key="StatusBarIcon">M22,9v6c0,1.1-0.9,2-2,2h-1l0-2h1V9H4v6h6v2H4c-1.1,0-2-0.9-2-2V9c0-1.1,0.9-2,2-2h16C21.1,7,22,7.9,22,9z M14.04,17.99 c0.18,0.39,0.73,0.39,0.91,0l0.63-1.4l1.4-0.63c0.39-0.18,0.39-0.73,0-0.91l-1.4-0.63l-0.63-1.4c-0.18-0.39-0.73-0.39-0.91,0 l-0.63,1.4l-1.4,0.63c-0.39,0.18-0.39,0.73,0,0.91l1.4,0.63L14.04,17.99z M16.74,13.43c0.1,0.22,0.42,0.22,0.52,0l0.36-0.8 l0.8-0.36c0.22-0.1,0.22-0.42,0-0.52l-0.8-0.36l-0.36-0.8c-0.1-0.22-0.42-0.22-0.52,0l-0.36,0.8l-0.8,0.36 c-0.22,0.1-0.22,0.42,0,0.52l0.8,0.36L16.74,13.43z</Geometry>
|
||||
<Geometry x:Key="SearchIcon">M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z</Geometry>
|
||||
<Geometry x:Key="DirectoryIcon">M19.71,15.71l-3.59,3.59c-0.63,0.63-1.71,0.18-1.71-0.71V16h-7c-1.1,0-2-0.9-2-2V5c0-0.55,0.45-1,1-1h0c0.55,0,1,0.45,1,1 v9h7v-2.59c0-0.89,1.08-1.34,1.71-0.71l3.59,3.59C20.1,14.68,20.1,15.32,19.71,15.71z</Geometry>
|
||||
|
|
@ -36,7 +37,7 @@
|
|||
<Geometry x:Key="BugIcon">M14,12H10V10H14M14,16H10V14H14M20,8H17.19C16.74,7.22 16.12,6.55 15.37,6.04L17,4.41L15.59,3L13.42,5.17C12.96,5.06 12.5,5 12,5C11.5,5 11.04,5.06 10.59,5.17L8.41,3L7,4.41L8.62,6.04C7.88,6.55 7.26,7.22 6.81,8H4V10H6.09C6.04,10.33 6,10.66 6,11V12H4V14H6V15C6,15.34 6.04,15.67 6.09,16H4V18H6.81C7.85,19.79 9.78,21 12,21C14.22,21 16.15,19.79 17.19,18H20V16H17.91C17.96,15.67 18,15.34 18,15V14H20V12H18V11C18,10.66 17.96,10.33 17.91,10H20V8Z</Geometry>
|
||||
<Geometry x:Key="GiftIcon">M22 10.92L19.26 9.33C21.9 7.08 19.25 2.88 16.08 4.31L15.21 4.68L15.1 3.72C15 2.64 14.44 1.87 13.7 1.42C12.06 .467 9.56 1.12 9.16 3.5L6.41 1.92C5.45 1.36 4.23 1.69 3.68 2.65L2.68 4.38C2.4 4.86 2.57 5.47 3.05 5.75L10.84 10.25L12.34 7.65L14.07 8.65L12.57 11.25L20.36 15.75C20.84 16 21.46 15.86 21.73 15.38L22.73 13.65C23.28 12.69 22.96 11.47 22 10.92M12.37 5C11.5 5.25 10.8 4.32 11.24 3.55C11.5 3.07 12.13 2.91 12.61 3.18C13.38 3.63 13.23 4.79 12.37 5M17.56 8C16.7 8.25 16 7.32 16.44 6.55C16.71 6.07 17.33 5.91 17.8 6.18C18.57 6.63 18.42 7.79 17.56 8M20.87 16.88C21.28 16.88 21.67 16.74 22 16.5V20C22 21.11 21.11 22 20 22H4C2.9 22 2 21.11 2 20V11H10.15L11 11.5V20H13V12.65L19.87 16.61C20.17 16.79 20.5 16.88 20.87 16.88Z</Geometry>
|
||||
<Geometry x:Key="NoteIcon">M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM8 19h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1zm0-6h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1s.45 1 1 1zM7 6c0 .55.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1H8c-.55 0-1 .45-1 1z</Geometry>
|
||||
<Geometry x:Key="InfoIcon">M11.5,2C6.81,2,3,5.81,3,10.5S6.81,19,11.5,19H12v3c4.86-2.34,8-7,8-11.5C20,5.81,16.19,2,11.5,2z M11.48,16 c-0.59,0-1.05-0.47-1.05-1.05c0-0.59,0.47-1.04,1.05-1.04c0.59,0,1.04,0.45,1.04,1.04C12.52,15.53,12.08,16,11.48,16z M13.99,9.83 c-0.63,0.93-1.23,1.21-1.56,1.81c-0.08,0.14-0.13,0.26-0.16,0.49c-0.05,0.39-0.36,0.68-0.75,0.68h-0.03 c-0.44,0-0.79-0.38-0.75-0.82c0.03-0.28,0.09-0.57,0.25-0.84c0.41-0.73,1.18-1.16,1.63-1.8c0.48-0.68,0.21-1.94-1.14-1.94 c-0.61,0-1.01,0.32-1.26,0.7c-0.19,0.29-0.57,0.39-0.89,0.25l0,0c-0.42-0.18-0.6-0.7-0.34-1.07C9.5,6.55,10.35,6,11.47,6 c1.23,0,2.08,0.56,2.51,1.26C14.34,7.87,14.56,8.99,13.99,9.83z</Geometry>
|
||||
<Geometry x:Key="InfoIcon">M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 15c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1zm1-8h-2V7h2v2z</Geometry>
|
||||
<Geometry x:Key="TrashIcon">M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M9.17,12.59c-0.39-0.39-0.39-1.02,0-1.41c0.39-0.39,1.02-0.39,1.41,0 L12,12.59l1.41-1.41c0.39-0.39,1.02-0.39,1.41,0s0.39,1.02,0,1.41L13.41,14l1.41,1.41c0.39,0.39,0.39,1.02,0,1.41 s-1.02,0.39-1.41,0L12,15.41l-1.41,1.41c-0.39,0.39-1.02,0.39-1.41,0c-0.39-0.39-0.39-1.02,0-1.41L10.59,14L9.17,12.59z M18,4h-2.5 l-0.71-0.71C14.61,3.11,14.35,3,14.09,3H9.91c-0.26,0-0.52,0.11-0.7,0.29L8.5,4H6C5.45,4,5,4.45,5,5s0.45,1,1,1h12 c0.55,0,1-0.45,1-1S18.55,4,18,4z</Geometry>
|
||||
<Geometry x:Key="HomeIcon">M18,4v16H6V4H18 M18,2H6C4.9,2,4,2.9,4,4v16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2L18,2z M7,19h10v-6H7 V19z M10,10h4v1h3V5H7v6h3V10z</Geometry>
|
||||
<Geometry x:Key="RegexIcon">M16,16.92C15.67,16.97 15.34,17 15,17C14.66,17 14.33,16.97 14,16.92V13.41L11.5,15.89C11,15.5 10.5,15 10.11,14.5L12.59,12H9.08C9.03,11.67 9,11.34 9,11C9,10.66 9.03,10.33 9.08,10H12.59L10.11,7.5C10.3,7.25 10.5,7 10.76,6.76V6.76C11,6.5 11.25,6.3 11.5,6.11L14,8.59V5.08C14.33,5.03 14.66,5 15,5C15.34,5 15.67,5.03 16,5.08V8.59L18.5,6.11C19,6.5 19.5,7 19.89,7.5L17.41,10H20.92C20.97,10.33 21,10.66 21,11C21,11.34 20.97,11.67 20.92,12H17.41L19.89,14.5C19.7,14.75 19.5,15 19.24,15.24V15.24C19,15.5 18.75,15.7 18.5,15.89L16,13.41V16.92H16V16.92M5,19A2,2 0 0,1 7,17A2,2 0 0,1 9,19A2,2 0 0,1 7,21A2,2 0 0,1 5,19H5Z</Geometry>
|
||||
|
|
@ -65,7 +66,12 @@
|
|||
<Geometry x:Key="LocateMeIcon">M11.71,17.99C8.53,17.84,6,15.22,6,12c0-3.31,2.69-6,6-6c3.22,0,5.84,2.53,5.99,5.71l-2.1-0.63C15.48,9.31,13.89,8,12,8 c-2.21,0-4,1.79-4,4c0,1.89,1.31,3.48,3.08,3.89L11.71,17.99z M22,12c0,0.3-0.01,0.6-0.04,0.9l-1.97-0.59C20,12.21,20,12.1,20,12 c0-4.42-3.58-8-8-8s-8,3.58-8,8s3.58,8,8,8c0.1,0,0.21,0,0.31-0.01l0.59,1.97C12.6,21.99,12.3,22,12,22C6.48,22,2,17.52,2,12 C2,6.48,6.48,2,12,2S22,6.48,22,12z M18.23,16.26l2.27-0.76c0.46-0.15,0.45-0.81-0.01-0.95l-7.6-2.28 c-0.38-0.11-0.74,0.24-0.62,0.62l2.28,7.6c0.14,0.47,0.8,0.48,0.95,0.01l0.76-2.27l3.91,3.91c0.2,0.2,0.51,0.2,0.71,0l1.27-1.27 c0.2-0.2,0.2-0.51,0-0.71L18.23,16.26z</Geometry>
|
||||
<Geometry x:Key="MeshIcon">M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm8 7h-5v12c0 .55-.45 1-1 1s-1-.45-1-1v-5h-2v5c0 .55-.45 1-1 1s-1-.45-1-1V9H4c-.55 0-1-.45-1-1s.45-1 1-1h16c.55 0 1 .45 1 1s-.45 1-1 1z</Geometry>
|
||||
<Geometry x:Key="MaterialIcon">M11 9h2v2h-2V9zm-2 2h2v2H9v-2zm4 0h2v2h-2v-2zm2-2h2v2h-2V9zM7 9h2v2H7V9zm12-6H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 18H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm2-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v5z</Geometry>
|
||||
|
||||
<Geometry x:Key="VisibleIcon">M12 4C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z</Geometry>
|
||||
<Geometry x:Key="NotVisibleIcon">M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM2.71 3.16c-.39.39-.39 1.02 0 1.41l1.97 1.97C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l2.72 2.72c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L4.13 3.16c-.39-.39-1.03-.39-1.42 0zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33c-.15-1.4-1.25-2.49-2.64-2.64l2.64 2.64z</Geometry>
|
||||
<Geometry x:Key="WireframeIcon">M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM8 17h8c.55 0 1-.45 1-1V8c0-.55-.45-1-1-1H8c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1zm1-8h6v6H9V9z</Geometry>
|
||||
<Geometry x:Key="NotWireframeIcon">M3,13h2v-2H3V13z M7,21h2v-2H7V21z M13,3h-2v2h2V3z M19,3v2h2C21,3.9,20.1,3,19,3z M5,21v-2H3C3,20.1,3.9,21,5,21z M3,17h2 v-2H3V17z M11,21h2v-2h-2V21z M19,13h2v-2h-2V13z M19,9h2V7h-2V9z M15,5h2V3h-2V5z M7.83,5L7,4.17V3h2v2H7.83z M19.83,17L19,16.17 V15h2v2H19.83z M9,15v-3.17L12.17,15H9z M2.1,3.51c-0.39,0.39-0.39,1.02,0,1.41L4.17,7H3v2h2V7.83l2,2V16c0,0.55,0.45,1,1,1h6.17 l2,2H15v2h2v-1.17l2.07,2.07c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L3.51,3.51C3.12,3.12,2.49,3.12,2.1,3.51z M17,8c0-0.55-0.45-1-1-1H9.83l2,2H15v3.17l2,2V8z</Geometry>
|
||||
<Geometry x:Key="SwapIcon">M16 17.01V11c0-.55-.45-1-1-1s-1 .45-1 1v6.01h-1.79c-.45 0-.67.54-.35.85l2.79 2.78c.2.19.51.19.71 0l2.79-2.78c.32-.31.09-.85-.35-.85H16zM8.65 3.35L5.86 6.14c-.32.31-.1.85.35.85H8V13c0 .55.45 1 1 1s1-.45 1-1V6.99h1.79c.45 0 .67-.54.35-.85L9.35 3.35c-.19-.19-.51-.19-.7 0z</Geometry>
|
||||
|
||||
<Style x:Key="TabItemFillSpace" TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
|
||||
<Setter Property="Width">
|
||||
<Setter.Value>
|
||||
|
|
@ -110,7 +116,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="CustomVerticalSeparator" TargetType="{x:Type Separator}" BasedOn="{StaticResource CustomSeparator}">
|
||||
<Setter Property="Margin" Value="8 0"/>
|
||||
<Setter Property="LayoutTransform">
|
||||
|
|
@ -150,7 +156,7 @@
|
|||
<ColumnDefinition Width="25" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Image x:Name="ListImage" Source="/FModel;component/Resources/archive.png"
|
||||
Width="16" Height="16" HorizontalAlignment="Center" Margin="0 0 3 0" />
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Left" Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
|
||||
|
|
@ -197,7 +203,7 @@
|
|||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="AudioFilesListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
|
||||
<Setter Property="ItemsSource" Value="{Binding AudioPlayer.AudioFilesView, IsAsync=True}" />
|
||||
<Setter Property="SelectedItem" Value="{Binding AudioPlayer.SelectedAudioFile}" />
|
||||
|
|
@ -221,7 +227,7 @@
|
|||
<ColumnDefinition Width="25" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Column="0" HorizontalAlignment="Left" Text="{Binding FileName}" TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Grid.Column="2" HorizontalAlignment="Right" Text="{Binding Length, Converter={x:Static converters:SizeToStringConverter.Instance}}" />
|
||||
</Grid>
|
||||
|
|
@ -496,7 +502,7 @@
|
|||
Grid.ColumnSpan="2"/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
|
||||
|
||||
<Trigger Property="IsExpanded" Value="False">
|
||||
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
|
|
@ -566,7 +572,7 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Image x:Name="ListImage" Source="/FModel;component/Resources/unknown_asset.png"
|
||||
Width="16" Height="16" HorizontalAlignment="Center" Margin="0 0 3 0" />
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Left" Text="{Binding FullPath, Converter={x:Static converters:FullPathToFileConverter.Instance}}" />
|
||||
|
|
@ -612,6 +618,86 @@
|
|||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MaterialsListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
|
||||
<Setter Property="ItemsSource" Value="{Binding ModelViewer.SelectedModel.Group3d, IsAsync=True}" />
|
||||
<Setter Property="SelectedItem" Value="{Binding ModelViewer.SelectedModel.SelectedGeometry}" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarExpansionMode" Value="NeverExpand"/>
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarPlacement" Value="Docked"/>
|
||||
<Setter Property="ItemTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="25" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Source="/FModel;component/Resources/materialicon.png" Width="16" Height="16" Margin="5 0" HorizontalAlignment="Center" />
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
|
||||
<ToggleButton Grid.Column="3" IsChecked="{Binding IsRendering}" Padding="3" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path x:Name="SvgIcon1" Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource VisibleIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
<ToggleButton Grid.Column="4" IsChecked="{Binding FillMode, Converter={x:Static converters:BoolToFillModeConverter.Instance}}" Padding="3" Style="{DynamicResource {x:Static adonisUi:Styles.ToolbarToggleButton}}">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path x:Name="SvgIcon2" Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" Data="{StaticResource WireframeIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding IsRendering}" Value="True">
|
||||
<Setter TargetName="SvgIcon1" Property="Data" Value="{StaticResource VisibleIcon}" />
|
||||
<Setter TargetName="SvgIcon1" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsRendering}" Value="False">
|
||||
<Setter TargetName="SvgIcon1" Property="Data" Value="{StaticResource NotVisibleIcon}" />
|
||||
<Setter TargetName="SvgIcon1" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding FillMode}" Value="{x:Static sharpDx:FillMode.Solid}">
|
||||
<Setter TargetName="SvgIcon2" Property="Data" Value="{StaticResource WireframeIcon}" />
|
||||
<Setter TargetName="SvgIcon2" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding FillMode}" Value="{x:Static sharpDx:FillMode.Wireframe}">
|
||||
<Setter TargetName="SvgIcon2" Property="Data" Value="{StaticResource NotWireframeIcon}" />
|
||||
<Setter TargetName="SvgIcon2" Property="Fill" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemContainerStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ModelViewer.SelectedModel.Group3d.Count, FallbackValue=0}" Value="0">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Grid>
|
||||
<TextBlock Text="No material found in the mesh" FontWeight="SemiBold" TextAlignment="Center"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GameFilesTabControl" TargetType="TabControl" BasedOn="{StaticResource {x:Type TabControl}}">
|
||||
<Setter Property="ItemsSource" Value="{Binding CUE4Parse.TabControl.TabsItems, IsAsync=True}" />
|
||||
<Setter Property="SelectedItem" Value="{Binding CUE4Parse.TabControl.SelectedTab}" />
|
||||
|
|
@ -623,7 +709,7 @@
|
|||
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding SelectedItem.TabCommand, ElementName=TabControlName}" CommandParameter="Open_Properties"/>
|
||||
<MouseBinding MouseAction="MiddleClick" Command="{Binding SelectedItem.TabCommand, ElementName=TabControlName}" CommandParameter="{Binding}" />
|
||||
</DockPanel.InputBindings>
|
||||
|
||||
|
||||
<TextBlock DockPanel.Dock="Left" Text="{Binding Header}" TextTrimming="CharacterEllipsis" ToolTip="{Binding Header}">
|
||||
<TextBlock.Width>
|
||||
<MultiBinding Converter="{x:Static converters:TabSizeConverter.Instance}" ConverterParameter="6">
|
||||
|
|
@ -649,7 +735,7 @@
|
|||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" Grid.ColumnSpan="3" DataContext="{Binding SelectedItem, ElementName=TabControlName}" />
|
||||
<Image Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform"
|
||||
Visibility="{Binding SelectedItem.HasImage, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
|
|
@ -907,16 +993,16 @@
|
|||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
TextElement.Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type adonisControls:RippleHost}}}"
|
||||
RecognizesAccessKey="True"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="{Binding Margin, ElementName=ItemHeader}"/>
|
||||
|
||||
<TextBlock x:Name="RippleLayerInputGesturePresenter"
|
||||
Grid.Column="2"
|
||||
Text="{TemplateBinding InputGestureText}"
|
||||
Text="{TemplateBinding InputGestureText}"
|
||||
TextElement.Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type adonisControls:RippleHost}}}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="{Binding Margin, ElementName=InputGesturePresenter}"/>
|
||||
|
||||
<Viewbox x:Name="RippleLayerArrowPresenter"
|
||||
|
|
@ -959,16 +1045,16 @@
|
|||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"
|
||||
RecognizesAccessKey="True"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="4, 0"/>
|
||||
|
||||
<TextBlock x:Name="InputGesturePresenter"
|
||||
Grid.Column="3"
|
||||
Text="{TemplateBinding InputGestureText}"
|
||||
Text="{TemplateBinding InputGestureText}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="4, 0"/>
|
||||
|
||||
<Viewbox x:Name="ArrowPresenter"
|
||||
|
|
@ -1085,7 +1171,7 @@
|
|||
<Setter Property="BorderBrush" TargetName="SpotlightLayer" Value="{Binding Path=(adonisExtensions:CursorSpotlightExtension.BorderBrush), RelativeSource={RelativeSource FindAncestor, AncestorType=MenuItem}}"/>
|
||||
<Setter Property="BorderThickness" TargetName="SpotlightLayer" Value="{Binding BorderThickness, RelativeSource={RelativeSource FindAncestor, AncestorType=MenuItem}}"/>
|
||||
</MultiTrigger>
|
||||
|
||||
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="Role" Value="TopLevelItem"/>
|
||||
|
|
@ -1158,7 +1244,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="CustomExpanderDownHeaderStyle" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
|
|
@ -1174,7 +1260,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="{x:Type Expander}" TargetType="Expander" BasedOn="{StaticResource {x:Type Expander}}">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsLoggerExpanded, Source={x:Static local:Settings.UserSettings.Default}}"></Setter>
|
||||
<Setter Property="Template">
|
||||
|
|
@ -1240,7 +1326,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiTrigger>
|
||||
|
||||
|
||||
<Trigger Property="IsExpanded" Value="True">
|
||||
<Setter Property="Margin" TargetName="HeaderSite" Value="0 0 0 5" />
|
||||
<Trigger.EnterActions>
|
||||
|
|
@ -1313,7 +1399,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="CustomRichTextBox" TargetType="RichTextBox">
|
||||
<Setter Property="Height" Value="175" />
|
||||
<Setter Property="IsReadOnly" Value="True" />
|
||||
|
|
@ -1324,7 +1410,7 @@
|
|||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Border x:Name="Bd"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="True"
|
||||
Background="{TemplateBinding Background}">
|
||||
|
|
@ -1333,7 +1419,7 @@
|
|||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
||||
|
||||
<Style.Resources>
|
||||
<Style TargetType="{x:Type Paragraph}">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
|
|
@ -1346,11 +1432,11 @@
|
|||
</Style>
|
||||
</Style.Resources>
|
||||
</Style>
|
||||
|
||||
|
||||
<ControlTemplate x:Key="CircleTemplate" TargetType="{x:Type controls:Magnifier}">
|
||||
<Grid>
|
||||
<Ellipse Fill="{TemplateBinding Background}" />
|
||||
<Ellipse Stroke="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
<Ellipse Stroke="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
StrokeThickness="{Binding Path=BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static converters:BorderThicknessToStrokeThicknessConverter.Instance}}">
|
||||
<Ellipse.Fill>
|
||||
<VisualBrush x:Name="PART_VisualBrush" ViewboxUnits="Absolute" Visual="{Binding Path=Target, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
|
|
@ -1372,7 +1458,7 @@
|
|||
|
||||
<Style TargetType="{x:Type controls:Magnifier}">
|
||||
<Setter Property="IsHitTestVisible" Value="False" />
|
||||
|
||||
|
||||
<Style.Triggers>
|
||||
<Trigger Property="FrameType" Value="Circle">
|
||||
<Setter Property="Template" Value="{StaticResource CircleTemplate}" />
|
||||
|
|
@ -1385,7 +1471,7 @@
|
|||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
</Style>
|
||||
|
|
@ -1397,13 +1483,13 @@
|
|||
<Style x:Key="{x:Type Button}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="ScrollViewer" BasedOn="{StaticResource {x:Type ScrollViewer}}">
|
||||
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled" />
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarExpansionMode" Value="AlwaysExpand"/>
|
||||
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarPlacement" Value="Docked"/>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}" BasedOn="{StaticResource {x:Type ScrollBar}}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Orientation" Value="Horizontal">
|
||||
|
|
@ -1434,7 +1520,7 @@
|
|||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="PlayPauseToolbarButton" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
|
|
@ -1452,7 +1538,7 @@
|
|||
<Setter Property="ToolTip" Value="Play" />
|
||||
|
||||
<Style.Triggers>
|
||||
|
||||
|
||||
<DataTrigger Binding="{Binding AudioPlayer.PlayedFile.PlaybackState, Mode=OneWay}" Value="{x:Static soundOut:PlaybackState.Playing}">
|
||||
<Setter Property="ToolTip" Value="Pause" />
|
||||
<Setter Property="Content">
|
||||
|
|
@ -1529,7 +1615,7 @@
|
|||
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style TargetType="{x:Type audioControls:Timeline}">
|
||||
<Setter Property="ProgressBrush">
|
||||
<Setter.Value>
|
||||
|
|
@ -1561,12 +1647,12 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Rectangle Name="PART_MousePosition" Grid.Row="0" Grid.RowSpan="2" Grid.Column="0"
|
||||
Fill="{TemplateBinding MousePositionBrush}" Width="2" Visibility="Collapsed" />
|
||||
<Grid Name="PART_Length" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" ClipToBounds="True" SnapsToDevicePixels="False" />
|
||||
<Grid Name="PART_ControlContainer" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
|
||||
<Border Name="PART_ProgressLine" BorderBrush="{TemplateBinding ProgressLineBrush}"
|
||||
<Border Name="PART_ProgressLine" BorderBrush="{TemplateBinding ProgressLineBrush}"
|
||||
Background="{TemplateBinding ProgressBrush}" Width="{TemplateBinding ActualWidth}"
|
||||
VerticalAlignment="Stretch" HorizontalAlignment="Left" BorderThickness="0 0 1 0" />
|
||||
</Grid>
|
||||
|
|
@ -1576,7 +1662,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style TargetType="{x:Type audioControls:SpectrumAnalyzer}">
|
||||
<Setter Property="FrequencyBarCount" Value="75" />
|
||||
<Setter Property="FrequencyBarBorderBrush" Value="#DAE5F2" />
|
||||
|
|
@ -1603,7 +1689,7 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style TargetType="{x:Type audioControls:Timeclock}">
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
|
|
@ -1620,9 +1706,9 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Name="PART_Label"
|
||||
Text="{TemplateBinding Label}"
|
||||
|
||||
<TextBlock Name="PART_Label"
|
||||
Text="{TemplateBinding Label}"
|
||||
FontFamily="{TemplateBinding LabelFont}"
|
||||
Padding="0"
|
||||
Foreground="{TemplateBinding LabelForeground}">
|
||||
|
|
@ -1630,8 +1716,8 @@
|
|||
<ScaleTransform ScaleX=".75" ScaleY=".75" />
|
||||
</TextBlock.LayoutTransform>
|
||||
</TextBlock>
|
||||
<TextBlock Name="PART_Time"
|
||||
Text="00:00:00.00"
|
||||
<TextBlock Name="PART_Time"
|
||||
Text="00:00:00.00"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
FontFamily="{TemplateBinding TimeFont}"
|
||||
|
|
@ -1641,12 +1727,12 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style TargetType="{x:Type folding:FoldingMargin}">
|
||||
<Setter Property="FoldingMarkerBrush" Value="#262630" />
|
||||
<Setter Property="FoldingMarkerBackgroundBrush" Value="#262630" />
|
||||
<Setter Property="SelectedFoldingMarkerBrush" Value="#808080" />
|
||||
<Setter Property="SelectedFoldingMarkerBackgroundBrush" Value="#2A2B34" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Resources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
|
||||
<controls:OnTagDataTemplateSelector x:Key="TagTemplateSelector" />
|
||||
<DataTemplate x:Key="GeneralTemplate">
|
||||
<Grid adonisExtensions:LayerExtension.Layer="2">
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Output Directory *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBox Grid.Row="0" Grid.Column="2" Text="{Binding OutputDirectory, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 5" />
|
||||
<Button Grid.Row="0" Grid.Column="4" Content="..." HorizontalAlignment="Right" Click="OnBrowseOutput" Margin="0 0 0 5" />
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Game Directory *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBox Grid.Row="1" Grid.Column="2" Text="{Binding GameDirectory, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 5" />
|
||||
<Button Grid.Row="1" Grid.Column="4" Content="..." HorizontalAlignment="Right" Click="OnBrowseDirectories" Margin="0 0 0 5" />
|
||||
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Update Mode" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Receive updates each time a new release is pushed to GitHub Receive updates each time a new commit is pushed to GitHub" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UpdateModes}" SelectedItem="{Binding SettingsView.SelectedUpdateMode, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Assets Language" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Language used and shown when parsing assets" />
|
||||
<ComboBox Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.AssetLanguages}" SelectedItem="{Binding SettingsView.SelectedAssetLanguage, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Discord Rich Presence" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DiscordRpcs}" SelectedItem="{Binding SettingsView.SelectedDiscordRpc, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -84,14 +84,14 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<Separator Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="5" Style="{StaticResource CustomSeparator}" Tag="ADVANCED"></Separator>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Presets" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Use a fine tuned preset for the game you're trying to load and its version" />
|
||||
<ComboBox Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.Presets}" SelectedItem="{Binding SettingsView.SelectedPreset, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" SelectionChanged="OnSelectionChanged" Margin="0 0 0 5">
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="7" Grid.Column="0" Text="UE4 Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="7" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeGames}" SelectedItem="{Binding SettingsView.SelectedUeGame, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" IsEnabled="{Binding SettingsView.EnableElements}"
|
||||
|
|
@ -102,7 +102,7 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="8" Grid.Column="0" Text="UE4 Object Versions *" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Override the UE4 object version to use when parsing assets" />
|
||||
<ComboBox Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.UeVersions}" SelectedItem="{Binding SettingsView.SelectedUeVersion, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" IsEnabled="{Binding SettingsView.EnableElements}"
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Versioning Configuration *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<Grid Grid.Row="9" Grid.Column="2" Grid.ColumnSpan="3" Margin="0 0 0 5">
|
||||
<Grid.ColumnDefinitions>
|
||||
|
|
@ -121,21 +121,16 @@
|
|||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Button Grid.Column="0" Content="Custom Versions" Click="OpenCustomVersions" />
|
||||
<Button Grid.Column="2" Content="Options" Click="OpenOptions" />
|
||||
</Grid>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Keep Directory Structure" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Auto Export & Save assets inside their game directory" />
|
||||
<ComboBox Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.DirectoryStructures}" SelectedItem="{Binding SettingsView.SelectedDirectoryStructure, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox Grid.Row="10" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
IsChecked="{Binding KeepDirectoryStructure, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" Margin="0 5 0 10"/>
|
||||
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Compressed Audio" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="What to do when encountering a compressed audio file" />
|
||||
<ComboBox Grid.Row="11" Grid.Column="2" Grid.ColumnSpan="3" ItemsSource="{Binding SettingsView.CompressedAudios}" SelectedItem="{Binding SettingsView.SelectedCompressedAudio, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -145,7 +140,7 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="12" 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}}}">
|
||||
<TextBlock.Style>
|
||||
|
|
@ -226,7 +221,7 @@
|
|||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Cosmetic Style" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding SettingsView.CosmeticStyles}" SelectedItem="{Binding SettingsView.SelectedCosmeticStyle, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -236,16 +231,11 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Cosmetic Shop Icon" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding SettingsView.CosmeticDisplayAssets}" SelectedItem="{Binding SettingsView.SelectedCosmeticDisplayAsset, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<CheckBox Grid.Row="1" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
IsChecked="{Binding CosmeticDisplayAsset, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" Margin="0 5 0 10"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
|
@ -269,7 +259,7 @@
|
|||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Mesh Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding SettingsView.MeshExportFormats}" SelectedItem="{Binding SettingsView.SelectedMeshExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -279,7 +269,7 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Level Of Detail Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding SettingsView.LodExportFormats}" SelectedItem="{Binding SettingsView.SelectedLodExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
|
|
@ -289,9 +279,16 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Texture Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{Binding SettingsView.TextureExportFormats}" SelectedItem="{Binding SettingsView.SelectedTextureExportFormat, Mode=TwoWay}"
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Save Skeletons as Empty Meshes" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<CheckBox Grid.Row="2" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
IsChecked="{Binding SaveSkeletonAsMesh, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" Margin="0 5 0 10"/>
|
||||
|
||||
<Separator Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Texture Format" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="4" Grid.Column="2" ItemsSource="{Binding SettingsView.TextureExportFormats}" SelectedItem="{Binding SettingsView.SelectedTextureExportFormat, Mode=TwoWay}"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Margin="0 0 0 5">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
|
@ -323,23 +320,22 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Left Switch on Directory Tab" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="0" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding DirLeftTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Right Switch on Directory Tab" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="1" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding DirRightTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
|
||||
<Separator Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Left Switch on Asset Tab" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="3" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AssetLeftTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
|
@ -352,9 +348,9 @@
|
|||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Remove Selected Asset Tab" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="6" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding AssetRemoveTab, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
|
||||
<Separator Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
|
||||
<TextBlock Grid.Row="8" Grid.Column="0" Text="Auto Export Data *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="8" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoExportData, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
|
@ -364,32 +360,29 @@
|
|||
<TextBlock Grid.Row="10" Grid.Column="0" Text="Auto Save Textures *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="10" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveTextures, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Auto Save Materials *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<TextBlock Grid.Row="11" Grid.Column="0" Text="Auto Save Animations *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="11" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveMaterials, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Save Meshes *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Save Animations *" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AutoSaveAnimations, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="14" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="14" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
<TextBlock Grid.Row="12" Grid.Column="0" Text="Auto Open Sounds *" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="12" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding AutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
<Separator Grid.Row="15" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="16" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="13" Grid.Column="0" Text="Auto Open Meshes & Materials *" VerticalAlignment="Center" />
|
||||
<controls:HotkeyTextBox Grid.Row="13" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}"
|
||||
HotKey="{Binding AutoOpenMeshes, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
|
||||
<Separator Grid.Row="14" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="15" Grid.Column="0" Text="Add Audio File" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="15" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding AddAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="17" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="17" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Play / Pause Current Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="16" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PlayPauseAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="18" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="17" Grid.Column="0" Text="Previous Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="17" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding PreviousAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
<TextBlock Grid.Row="19" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="19" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="Next Audio" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<controls:HotkeyTextBox Grid.Row="18" Grid.Column="2" Style="{StaticResource TextBoxDefaultStyle}" Margin="0 0 0 5"
|
||||
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
|
@ -404,7 +397,7 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<TreeView Grid.Row="0" Grid.Column="0" x:Name="SettingsTree" SelectedItemChanged="OnSelectedItemChanged"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer3BackgroundBrush}}">
|
||||
<TreeViewItem Tag="GeneralTemplate">
|
||||
|
|
@ -471,11 +464,11 @@
|
|||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
</TreeView>
|
||||
|
||||
|
||||
<Grid Grid.Row="0" Grid.Column="1" Margin="{adonisUi:Space 1, 0.5}" HorizontalAlignment="Stretch">
|
||||
<ContentControl ContentTemplateSelector="{StaticResource TagTemplateSelector}" Content="{Binding SelectedItem.Tag, ElementName=SettingsTree}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" adonisExtensions:LayerExtension.IncreaseLayer="True"
|
||||
Background="{DynamicResource {x:Static adonisUi:Brushes.Layer1BackgroundBrush}}">
|
||||
<Grid Margin="30, 12, 6, 12">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Windows;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
|
|
@ -118,8 +118,8 @@ namespace FModel.Views
|
|||
var result = dictionary.ShowDialog();
|
||||
if (!result.HasValue || !result.Value)
|
||||
return;
|
||||
|
||||
|
||||
_applicationView.SettingsView.SelectedOptions = dictionary.Options;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
# FModel [](https://discord.gg/fdkNYYQ)
|
||||
|
||||
    Open-source software for exploring Unreal Engine games' files. From seeing the properties of an asset to listening to your favorite audio files, it has never been easier to navigate inside a game's assets. FModel supports tens of file types and asset types, to display the most information possible and it also has the ability to let you <kbd>CTRL+LMB</kbd> on an asset path to display its properties too.
|
||||
Beginner-friendly and open-source software for data-mining games made with Unreal Engine.
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/26126862/119065662-52534800-b9de-11eb-85fd-a47797daa062.png" align="center" alt="FModel">
|
||||
|
||||
## Installation
|
||||
|
||||
To use FModel, you need to have **[.NET 5](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-5.0.11-windows-x64-installer)** installed on your computer
|
||||
* **[Download](https://github.com/iAmAsval/FModel/releases/latest/download/FModel.zip)** the latest release.
|
||||
* **Extract FModel.exe** somewhere on your PC and launch it
|
||||
Follow [our documentation](https://fmodel.app/docs#installation)
|
||||
|
||||
## Authors
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user