mirror of
https://github.com/4sval/FModel.git
synced 2026-05-09 04:31:55 -05:00
Merge remote-tracking branch 'upstream/dev' into ExportingFix
This commit is contained in:
commit
d8d8757768
|
|
@ -1 +1 @@
|
|||
Subproject commit 39d1b4b1fb57cc7cf47e10b003a1dca3961661c3
|
||||
Subproject commit a3821bdc34ef75f10a2003092ad2502dc648e53a
|
||||
|
|
@ -161,4 +161,5 @@ public enum EAssetCategory : uint
|
|||
GameSpecific = AssetCategoryExtensions.CategoryBase + (10 << 16),
|
||||
Borderlands = GameSpecific + 1,
|
||||
Aion2 = GameSpecific + 2,
|
||||
RocoKingdomWorld = GameSpecific + 3,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
|
|
@ -702,23 +703,11 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
|||
public static bool TryConvert(string inputFilePath, byte[] inputFileData, out string wavFilePath, bool updateUi = false)
|
||||
{
|
||||
wavFilePath = string.Empty;
|
||||
var vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "test.exe");
|
||||
if (!File.Exists(vgmFilePath))
|
||||
{
|
||||
vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-cli.exe");
|
||||
if (!File.Exists(vgmFilePath))
|
||||
{
|
||||
Log.Error("Failed to convert {InputFilePath}, vgmstream is missing", inputFilePath);
|
||||
FLogger.Append(ELog.Error, () =>
|
||||
{
|
||||
FLogger.Text("Failed to convert audio because vgmstream is missing. See: ", Constants.WHITE);
|
||||
FLogger.Link("→ link ←", Constants.AUDIO_ISSUE_LINK, true);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var vgmStreamPath = TryGetVgmstreamPath();
|
||||
if (string.IsNullOrEmpty(vgmStreamPath))
|
||||
return false;
|
||||
|
||||
var success = TryConvertToWAV(inputFilePath, inputFileData, vgmFilePath, true, out wavFilePath);
|
||||
var success = TryConvertToWav(inputFilePath, inputFileData, vgmStreamPath, true, out wavFilePath);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
|
@ -751,10 +740,10 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
|||
return false;
|
||||
}
|
||||
|
||||
return TryConvertToWAV(SelectedAudioFile.FilePath, SelectedAudioFile.Data, decoderPath, false, out rawFilePath);
|
||||
return TryConvertToWav(SelectedAudioFile.FilePath, SelectedAudioFile.Data, decoderPath, false, out rawFilePath);
|
||||
}
|
||||
|
||||
private static bool TryConvertToWAV(string inputFilePath, byte[] inputFileData, string converterPath, bool usevgmstream, out string wavFilePath)
|
||||
private static bool TryConvertToWav(string inputFilePath, byte[] inputFileData, string converterPath, bool usevgmstream, out string wavFilePath)
|
||||
{
|
||||
wavFilePath = Path.ChangeExtension(inputFilePath, ".wav");
|
||||
var directory = Path.GetDirectoryName(inputFilePath);
|
||||
|
|
@ -784,4 +773,74 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
|||
|
||||
return success;
|
||||
}
|
||||
|
||||
private static string TryGetVgmstreamPath()
|
||||
{
|
||||
var vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "test.exe");
|
||||
if (!File.Exists(vgmFilePath))
|
||||
{
|
||||
vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-cli.exe");
|
||||
if (!File.Exists(vgmFilePath))
|
||||
{
|
||||
Log.Error("Failed to convert audio, vgmstream is missing");
|
||||
FLogger.Append(ELog.Error, () =>
|
||||
{
|
||||
FLogger.Text("Failed to convert audio because vgmstream is missing. See: ", Constants.WHITE);
|
||||
FLogger.Link("→ link ←", Constants.AUDIO_ISSUE_LINK, true);
|
||||
});
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return vgmFilePath;
|
||||
}
|
||||
|
||||
// Since Square Enix soundbanks are pretty niche, let's just use vgmstream to extract them
|
||||
public static List<string> ExtractSquareEnixAudio(string sabPath, byte[] sqexData)
|
||||
{
|
||||
var vgmStreamPath = TryGetVgmstreamPath();
|
||||
if (string.IsNullOrEmpty(vgmStreamPath))
|
||||
return [];
|
||||
if (sqexData.Length == 0)
|
||||
return [];
|
||||
|
||||
var extractionDir = Path.GetDirectoryName(sabPath);
|
||||
Directory.CreateDirectory(extractionDir);
|
||||
|
||||
// There's no clean way to know what was extracted with vgmstream (it's a soundbank, might contain multiple sounds) so we're monitoring extraction directory
|
||||
var capturedFiles = new ConcurrentBag<string>();
|
||||
using var watcher = new FileSystemWatcher(extractionDir)
|
||||
{
|
||||
Filter = "*.wav",
|
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime
|
||||
};
|
||||
|
||||
void handler(object s, FileSystemEventArgs e) => capturedFiles.Add(e.FullPath);
|
||||
|
||||
watcher.Created += handler;
|
||||
watcher.Changed += handler;
|
||||
watcher.EnableRaisingEvents = true;
|
||||
|
||||
var tempSab = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".sab");
|
||||
File.WriteAllBytes(tempSab, sqexData);
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = vgmStreamPath,
|
||||
Arguments = $"-S 0 -o \"{extractionDir}\\?n_?s.wav\" \"{tempSab}\"",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
process?.WaitForExit(15000);
|
||||
}
|
||||
|
||||
File.Delete(tempSab);
|
||||
watcher.EnableRaisingEvents = false;
|
||||
|
||||
return [.. capturedFiles.Distinct()];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,12 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
using AdonisUI.Controls;
|
||||
|
||||
using CUE4Parse;
|
||||
using CUE4Parse.Compression;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
|
|
@ -22,11 +20,13 @@ using CUE4Parse.FileProvider.Vfs;
|
|||
using CUE4Parse.GameTypes.Aion2.Objects;
|
||||
using CUE4Parse.GameTypes.AoC.Objects;
|
||||
using CUE4Parse.GameTypes.AshEchoes.FileProvider;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.GameTypes.KRD.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.Borderlands3.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.Borderlands4.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.Borderlands4.Wwise;
|
||||
using CUE4Parse.GameTypes.Borderlands3.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.KRD.Assets.Exports;
|
||||
using CUE4Parse.GameTypes.RocoKingdomWorld.Assets.Objects;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.GameTypes.SquareEnix.UE4.Assets.Exports;
|
||||
using CUE4Parse.MappingsProvider;
|
||||
using CUE4Parse.UE4.AssetRegistry;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
|
|
@ -57,14 +57,11 @@ using CUE4Parse.UE4.Shaders;
|
|||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse.UE4.Wwise;
|
||||
using CUE4Parse.Utils;
|
||||
|
||||
using CUE4Parse_Conversion;
|
||||
using CUE4Parse_Conversion.Sounds;
|
||||
|
||||
using EpicManifestParser;
|
||||
using EpicManifestParser.UE;
|
||||
using EpicManifestParser.ZlibngDotNetDecompressor;
|
||||
|
||||
using FModel.Creator;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
|
|
@ -73,21 +70,14 @@ using FModel.Settings;
|
|||
using FModel.Views;
|
||||
using FModel.Views.Resources.Controls;
|
||||
using FModel.Views.Snooper;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
using Svg.Skia;
|
||||
|
||||
using UE4Config.Parsing;
|
||||
|
||||
using Application = System.Windows.Application;
|
||||
using FGuid = CUE4Parse.UE4.Objects.Core.Misc.FGuid;
|
||||
|
||||
|
|
@ -695,6 +685,11 @@ public class CUE4ParseViewModel : ViewModel
|
|||
ProcessAion2DatFile(entry, updateUi, saveProperties);
|
||||
break;
|
||||
}
|
||||
case "bytes" when Provider.Versions.Game is EGame.GAME_RocoKingdomWorld:
|
||||
{
|
||||
ProcessRocoBinFile(entry, updateUi, saveProperties);
|
||||
break;
|
||||
}
|
||||
case "dbc" when Provider.Versions.Game is EGame.GAME_AshesOfCreation:
|
||||
{
|
||||
ProcessCacheDBFile(entry, updateUi, saveProperties);
|
||||
|
|
@ -981,6 +976,40 @@ public class CUE4ParseViewModel : ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
// Roco Kingdom: World
|
||||
void ProcessRocoBinFile(GameFile entry, bool updateUi, bool saveProperties)
|
||||
{
|
||||
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json");
|
||||
var nonFileName = "/" + entry.NameWithoutExtension + ".non";
|
||||
var nonPath = Provider.Files.Keys.FirstOrDefault(k => k.EndsWith(nonFileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// I will only get one localization file because they did not translate any languages, lol
|
||||
var locPathKey = entry.Path.Replace("/BinData/", "/BinLocalize/en_US/").Replace("/BinDataCompressed/", "/BinLocalize/en_US/");
|
||||
var locFileFound = Provider.Files.TryGetValue(locPathKey, out var locEntry);
|
||||
|
||||
if (!string.IsNullOrEmpty(nonPath) && Provider.Files.TryGetValue(nonPath, out var nonEntry))
|
||||
{
|
||||
string json = Encoding.UTF8.GetString(nonEntry.Read());
|
||||
var schema = JsonConvert.DeserializeObject<FRocoSchema>(json);
|
||||
var archive = entry.CreateReader();
|
||||
var locArchive = locFileFound ? new FRocoBinData(locEntry.CreateReader(), null, ERocoBinDataType.BinLocalize) : null;
|
||||
|
||||
var data = entry.PathWithoutExtension switch
|
||||
{
|
||||
var p when p.Contains("BinDataCompressed") => new FRocoBinData(archive, schema, ERocoBinDataType.BinDataCompressed, locArchive),
|
||||
var p when p.Contains("BinData") => new FRocoBinData(archive, schema, ERocoBinDataType.BinData, locArchive),
|
||||
var p when p.Contains("BinLocalize") => new FRocoBinData(archive, null, ERocoBinDataType.BinLocalize),
|
||||
_ => null
|
||||
};
|
||||
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(data, Formatting.Indented), saveProperties, updateUi);
|
||||
}
|
||||
else if (entry.PathWithoutExtension.Contains("/Bin/"))
|
||||
{
|
||||
throw new Exception($"Could not find associated .non file for {entry.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessAion2DatFile(GameFile entry, bool updateUi, bool saveProperties)
|
||||
{
|
||||
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json");
|
||||
|
|
@ -1171,20 +1200,20 @@ public class CUE4ParseViewModel : ViewModel
|
|||
case UFMODEvent when (isNone || saveAudio) && pointer.Object.Value is UFMODEvent fmodEvent:
|
||||
{
|
||||
var extractedSounds = FmodProvider.ExtractEventSounds(fmodEvent);
|
||||
var directory = Path.GetDirectoryName(fmodEvent.Owner?.Name) ?? "/FMOD/Desktop/";
|
||||
var directory = Path.GetDirectoryName(Provider.FixPath(fmodEvent.Owner?.Name ?? "/FMOD/Desktop/"));
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(cancellationToken, Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio, updateUi);
|
||||
SaveAndPlaySound(cancellationToken, Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio, updateUi);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UFMODBank when (isNone || saveAudio) && pointer.Object.Value is UFMODBank fmodBank:
|
||||
{
|
||||
var extractedSounds = FmodProvider.ExtractBankSounds(fmodBank);
|
||||
var directory = Path.GetDirectoryName(fmodBank.Owner?.Name) ?? "/FMOD/Desktop/";
|
||||
var directory = Path.GetDirectoryName(Provider.FixPath(fmodBank.Owner?.Name ?? "/FMOD/Desktop/"));
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(cancellationToken, Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio, updateUi);
|
||||
SaveAndPlaySound(cancellationToken, Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio, updateUi);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1207,6 +1236,22 @@ public class CUE4ParseViewModel : ViewModel
|
|||
}
|
||||
return false;
|
||||
}
|
||||
case USQEXSEADSoundBank or USQEXSEADSound when (isNone || saveAudio) && pointer.Object.Value is UObject squareEnixObject:
|
||||
{
|
||||
var data = squareEnixObject switch
|
||||
{
|
||||
USQEXSEADSoundBank sqexSoundBank => sqexSoundBank.SQEXSoundBankData?.Data ?? [],
|
||||
USQEXSEADSound sqexSound => sqexSound.SQEXSoundData?.Data ?? [],
|
||||
_ => [],
|
||||
};
|
||||
var sabPath = Path.Combine(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/').SubstringBeforeLast('/'), squareEnixObject.Name);
|
||||
var extractedSounds = AudioPlayerViewModel.ExtractSquareEnixAudio(sabPath, data);
|
||||
foreach (var soundPath in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(cancellationToken, soundPath, "wav", File.ReadAllBytes(soundPath), saveAudio, updateUi);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UAkMediaAssetData when isNone || saveAudio:
|
||||
case USoundWave when isNone || saveAudio:
|
||||
{
|
||||
|
|
@ -1443,8 +1488,10 @@ public class CUE4ParseViewModel : ViewModel
|
|||
private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool saveAudio, bool updateUi)
|
||||
{
|
||||
if (fullPath.StartsWith('/')) fullPath = fullPath[1..];
|
||||
var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory,
|
||||
UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}";
|
||||
var extLower = ext.ToLowerInvariant();
|
||||
var baseFilePath = UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/');
|
||||
var combinedPath = Path.Combine(UserSettings.Default.AudioDirectory, baseFilePath);
|
||||
var savedAudioPath = Path.ChangeExtension(combinedPath, extLower).Replace('\\', '/');
|
||||
|
||||
if (saveAudio)
|
||||
{
|
||||
|
|
@ -1453,7 +1500,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
Directory.CreateDirectory(directory);
|
||||
|
||||
bool conversionSuccess = true;
|
||||
if (UserSettings.Default.ConvertAudioOnBulkExport)
|
||||
if (UserSettings.Default.ConvertAudioOnBulkExport && extLower is not "wav")
|
||||
{
|
||||
if (AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath))
|
||||
savedAudioPath = wavFilePath;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using CUE4Parse.GameTypes.Borderlands4.Assets.Exports;
|
|||
using CUE4Parse.GameTypes.FN.Assets.Exports.DataAssets;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.GameTypes.SMG.UE4.Assets.Objects;
|
||||
using CUE4Parse.GameTypes.SquareEnix.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
|
|
@ -245,9 +246,9 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
|||
|
||||
UFMODBankLookup => (EAssetCategory.Data, EBulkType.None),
|
||||
|
||||
UFMODBus or UFMODSnapshot or UFMODSnapshotReverb or UFMODVCA => (EAssetCategory.Audio, EBulkType.None),
|
||||
UFMODBus or UFMODSnapshot or UFMODSnapshotReverb or UFMODVCA or USQEXSEADSoundAttenuation => (EAssetCategory.Audio, EBulkType.None),
|
||||
|
||||
UFMODBank or UAkAudioBank or UAtomWaveBank or UAkInitBank => (EAssetCategory.SoundBank, EBulkType.Audio),
|
||||
UFMODBank or UAkAudioBank or UAtomWaveBank or UAkInitBank or USQEXSEADSoundBank => (EAssetCategory.SoundBank, EBulkType.Audio),
|
||||
|
||||
UWwiseAssetLibrary or USoundBase or UAkMediaAssetData or UAtomCueSheet
|
||||
or USoundAtomCueSheet or UAkAudioType or UExternalSource or UExternalSourceBank
|
||||
|
|
@ -259,8 +260,8 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
|||
UNiagaraSystem or UNiagaraScriptBase or UParticleSystem => (EAssetCategory.Particle, EBulkType.None),
|
||||
|
||||
// Game specific assets below
|
||||
UBorderlandsDialogObject => (EAssetCategory.Borderlands, EBulkType.None), // Borderlands 3;
|
||||
UGbxGraphAsset or UDialogScriptData or UDialogPerformanceData => (EAssetCategory.Borderlands, EBulkType.Audio), // Borderlands 4; Borderlands 3;
|
||||
UBorderlandsDialogObject when GameVersion is EGame.GAME_Borderlands3 => (EAssetCategory.Borderlands, EBulkType.None), // Borderlands 3;
|
||||
UGbxGraphAsset or UDialogScriptData or UDialogPerformanceData when GameVersion is EGame.GAME_Borderlands4 or EGame.GAME_Borderlands3 => (EAssetCategory.Borderlands, EBulkType.Audio), // Borderlands 4; Borderlands 3;
|
||||
UFaceFXAnimSet when GameVersion is EGame.GAME_Borderlands4 => (EAssetCategory.Borderlands, EBulkType.Audio), // Borderlands 4;
|
||||
|
||||
_ => (EAssetCategory.All, EBulkType.None),
|
||||
|
|
@ -358,6 +359,7 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
|||
break;
|
||||
case "stinfo":
|
||||
case "ushaderbytecode":
|
||||
case "upipelinecache":
|
||||
AssetCategory = EAssetCategory.ByteCode;
|
||||
break;
|
||||
case "wav":
|
||||
|
|
@ -439,6 +441,11 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
|||
case "dat" when GameVersion is EGame.GAME_Aion2:
|
||||
AssetCategory = EAssetCategory.Aion2;
|
||||
break;
|
||||
case "bytes" when GameVersion is EGame.GAME_RocoKingdomWorld:
|
||||
case "non" when GameVersion is EGame.GAME_RocoKingdomWorld:
|
||||
case "cam" when GameVersion is EGame.GAME_RocoKingdomWorld:
|
||||
AssetCategory = EAssetCategory.RocoKingdomWorld;
|
||||
break;
|
||||
default:
|
||||
AssetCategory = EAssetCategory.All; // just so it sets resolved
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
<SolidColorBrush x:Key="FoliageBrush" Color="ForestGreen" />
|
||||
<SolidColorBrush x:Key="ParticleBrush" Color="Gold" />
|
||||
<SolidColorBrush x:Key="AnimationBrush" Color="Coral" />
|
||||
<SolidColorBrush x:Key="LuaBrush" Color="DarkBlue" />
|
||||
<SolidColorBrush x:Key="LuaBrush" Color="Blue" />
|
||||
<SolidColorBrush x:Key="JsonXmlBrush" Color="LightGreen" />
|
||||
<SolidColorBrush x:Key="CodeBrush" Color="SandyBrown" />
|
||||
<SolidColorBrush x:Key="HtmlBrush" Color="Tomato" />
|
||||
|
|
@ -52,4 +52,5 @@
|
|||
<!-- For specific games -->
|
||||
<SolidColorBrush x:Key="BorderlandsBrush" Color="Yellow"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AionBrush" Color="DeepSkyBlue"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="RocoKingdomWorldBrush" Color="#fecf4d"></SolidColorBrush>
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ public class FileToGeometryConverter : IMultiValueConverter
|
|||
|
||||
EAssetCategory.Borderlands => ("BorderlandsIcon", "BorderlandsBrush"),
|
||||
EAssetCategory.Aion2 => ("AionIcon", "AionBrush"),
|
||||
EAssetCategory.RocoKingdomWorld => ("RocoKingdomWorldIcon", "RocoKingdomWorldBrush"),
|
||||
|
||||
_ => ("AssetIcon", "NeutralBrush")
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user