full patched archive support
Some checks failed
FModel QA Builder / build (push) Has been cancelled

This commit is contained in:
Asval 2025-02-04 23:12:12 +01:00
parent a704612544
commit 3d0bdf05e1
32 changed files with 189 additions and 293 deletions

@ -1 +1 @@
Subproject commit 94dbeb7cd3232b6719120219ae7d2a63968e9036
Subproject commit ab6dff8e98e94335916a549810466eb4571a072b

View File

@ -2,9 +2,8 @@
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Reflection;
using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Extensions;
using CUE4Parse.Utils;
namespace FModel;
@ -12,7 +11,7 @@ public static class Constants
{
public static readonly string APP_PATH = Path.GetFullPath(Environment.GetCommandLineArgs()[0]);
public static readonly string APP_VERSION = FileVersionInfo.GetVersionInfo(APP_PATH).FileVersion;
public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion.SubstringAfter('+');
public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion?.SubstringAfter('+');
public static readonly string APP_SHORT_COMMIT_ID = APP_COMMIT_ID[..7];
public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000";

View File

@ -3,7 +3,7 @@ using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Objects;
using CUE4Parse.UE4.Objects.Core.i18N;
using CUE4Parse.UE4.Objects.UObject;
using FModel.Extensions;
using CUE4Parse.Utils;
using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;

View File

@ -7,7 +7,7 @@ using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Assets.Objects;
using CUE4Parse.UE4.Objects.Core.i18N;
using CUE4Parse.UE4.Objects.UObject;
using FModel.Extensions;
using CUE4Parse.Utils;
using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;

View File

@ -67,7 +67,7 @@ public class Typefaces
Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream);
switch (viewModel.Provider.InternalGameName.ToUpperInvariant())
switch (viewModel.Provider.ProjectName.ToUpperInvariant())
{
case "FORTNITEGAME":
{

View File

@ -12,6 +12,7 @@ using CUE4Parse.UE4.Objects.UObject;
using CUE4Parse.UE4.Versions;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.UE4.Assets.Objects;
using CUE4Parse.Utils;
using FModel.Framework;
using FModel.Extensions;
using FModel.Services;
@ -160,12 +161,7 @@ public static class Utils
// fullpath must be either without any extension or with the export objectname
public static bool TryLoadObject<T>(string fullPath, out T export) where T : UObject
{
return _applicationView.CUE4Parse.Provider.TryLoadObject(fullPath, out export);
}
public static IEnumerable<UObject> LoadExports(string packagePath)
{
return _applicationView.CUE4Parse.Provider.LoadAllObjects(packagePath);
return _applicationView.CUE4Parse.Provider.TryLoadPackageObject(fullPath, out export);
}
public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f)
@ -417,4 +413,4 @@ public static class Utils
return ret;
}
}
}

View File

@ -24,76 +24,6 @@ public static class StringExtensions
return $"{size:# ###.##} {sizes[order]}".TrimStart();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringBefore(this string s, char delimiter)
{
var index = s.IndexOf(delimiter);
return index == -1 ? s : s[..index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringBefore(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
{
var index = s.IndexOf(delimiter, comparisonType);
return index == -1 ? s : s[..index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringAfter(this string s, char delimiter)
{
var index = s.IndexOf(delimiter);
return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringAfter(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
{
var index = s.IndexOf(delimiter, comparisonType);
return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringBeforeLast(this string s, char delimiter)
{
var index = s.LastIndexOf(delimiter);
return index == -1 ? s : s[..index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringBeforeLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
{
var index = s.LastIndexOf(delimiter, comparisonType);
return index == -1 ? s : s[..index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringBeforeWithLast(this string s, char delimiter)
{
var index = s.LastIndexOf(delimiter);
return index == -1 ? s : s[..(index + 1)];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringBeforeWithLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
{
var index = s.LastIndexOf(delimiter, comparisonType);
return index == -1 ? s : s[..(index + 1)];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringAfterLast(this string s, char delimiter)
{
var index = s.LastIndexOf(delimiter);
return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SubstringAfterLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
{
var index = s.LastIndexOf(delimiter, comparisonType);
return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetNameLineNumber(this string s, string lineToFind)
{

View File

@ -6,7 +6,6 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using AdonisUI.Controls;
using FModel.Extensions;
using FModel.Services;
using FModel.Settings;
using FModel.ViewModels;
@ -267,7 +266,7 @@ public partial class MainWindow
return;
var filters = textBox.Text.Trim().Split(' ');
folder.AssetsList.AssetsView.Filter = o => { return o is AssetItem assetItem && filters.All(x => assetItem.FullPath.SubstringAfterLast('/').Contains(x, StringComparison.OrdinalIgnoreCase)); };
folder.AssetsList.AssetsView.Filter = o => { return o is AssetItem assetItem && filters.All(x => assetItem.FileName.Contains(x, StringComparison.OrdinalIgnoreCase)); };
}
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)

View File

@ -48,7 +48,7 @@ namespace FModel.Services
public void UpdatePresence(CUE4ParseViewModel viewModel) =>
UpdatePresence(
$"{viewModel.Provider.GameDisplayName ?? viewModel.Provider.InternalGameName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages",
$"{viewModel.Provider.GameDisplayName ?? viewModel.Provider.ProjectName} - {viewModel.Provider.MountedVfs.Count}/{viewModel.Provider.MountedVfs.Count + viewModel.Provider.UnloadedVfs.Count} Packages",
$"Mode: {UserSettings.Default.LoadingMode.GetDescription()} - {viewModel.SearchVm.ResultsCount:### ### ###} Loaded Assets".Trim());
public void UpdatePresence(string details, string state)

View File

@ -1,7 +1,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FModel.Extensions;
using CUE4Parse.Utils;
using FModel.Framework;
using FModel.ViewModels.ApiEndpoints.Models;
using Newtonsoft.Json.Linq;

View File

@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using AutoUpdaterDotNET;
using CUE4Parse.Utils;
using FModel.Extensions;
using FModel.Framework;
using FModel.Services;

View File

@ -1,6 +1,7 @@
using System.ComponentModel;
using System.Windows.Data;
using CUE4Parse.Compression;
using CUE4Parse.Utils;
using FModel.Framework;
namespace FModel.ViewModels;
@ -49,7 +50,33 @@ public class AssetItem : ViewModel
private set => SetProperty(ref _compression, value);
}
public AssetItem(string fullPath, bool isEncrypted, long offset, long size, string archive, CompressionMethod compression)
private string _directory;
public string Directory
{
get => _directory;
private set => SetProperty(ref _directory, value);
}
private string _fileName;
public string FileName
{
get => _fileName;
private set => SetProperty(ref _fileName, value);
}
private string _extension;
public string Extension
{
get => _extension;
private set => SetProperty(ref _extension, value);
}
public AssetItem(string titleExtra, AssetItem asset) : this(asset.FullPath, asset.IsEncrypted, asset.Offset, asset.Size, asset.Archive, asset.Compression)
{
FullPath += titleExtra;
}
public AssetItem(string fullPath, bool isEncrypted = false, long offset = 0, long size = 0, string archive = "", CompressionMethod compression = CompressionMethod.None)
{
FullPath = fullPath;
IsEncrypted = isEncrypted;
@ -57,6 +84,10 @@ public class AssetItem : ViewModel
Size = size;
Archive = archive;
Compression = compression;
Directory = FullPath.SubstringBeforeLast('/');
FileName = FullPath.SubstringAfterLast('/');
Extension = FullPath.SubstringAfterLast('.').ToLowerInvariant();
}
public override string ToString() => FullPath;

View File

@ -13,6 +13,7 @@ using System.Threading;
using System.Windows;
using System.Windows.Data;
using CSCore.CoreAudioAPI;
using CUE4Parse.Utils;
using FModel.Extensions;
using FModel.Framework;
using FModel.Services;

View File

@ -7,7 +7,6 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using CUE4Parse.FileProvider.Objects;
using CUE4Parse.UE4.VirtualFileSystem;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;

View File

@ -9,9 +9,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using AdonisUI.Controls;
using CUE4Parse.Compression;
using CUE4Parse.Encryption.Aes;
using CUE4Parse.FileProvider;
@ -37,15 +35,14 @@ using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.Shaders;
using CUE4Parse.UE4.Versions;
using CUE4Parse.UE4.Wwise;
using CUE4Parse_Conversion;
using CUE4Parse_Conversion.Sounds;
using CUE4Parse.UE4.Assets;
using CUE4Parse.UE4.Objects.UObject;
using CUE4Parse.Utils;
using EpicManifestParser;
using EpicManifestParser.UE;
using EpicManifestParser.ZlibngDotNetDecompressor;
using FModel.Creator;
using FModel.Extensions;
using FModel.Framework;
@ -54,18 +51,12 @@ using FModel.Settings;
using FModel.Views;
using FModel.Views.Resources.Controls;
using FModel.Views.Snooper;
using Newtonsoft.Json;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using Serilog;
using SkiaSharp;
using UE4Config.Parsing;
using Application = System.Windows.Application;
using FGuid = CUE4Parse.UE4.Objects.Core.Misc.FGuid;
@ -78,13 +69,6 @@ public class CUE4ParseViewModel : ViewModel
private readonly Regex _fnLiveRegex = new(@"^FortniteGame[/\\]Content[/\\]Paks[/\\]",
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
private string _internalGameName;
public string InternalGameName
{
get => _internalGameName;
set => SetProperty(ref _internalGameName, value);
}
private bool _modelIsOverwritingMaterial;
public bool ModelIsOverwritingMaterial
{
@ -153,20 +137,18 @@ public class CUE4ParseViewModel : ViewModel
{
case Constants._FN_LIVE_TRIGGER:
{
InternalGameName = "FortniteGame";
Provider = new StreamedFileProvider("FortniteLive", true, versionContainer);
break;
}
case Constants._VAL_LIVE_TRIGGER:
{
InternalGameName = "ShooterGame";
Provider = new StreamedFileProvider("ValorantLive", true, versionContainer);
break;
}
default:
{
InternalGameName = gameDirectory.SubstringBeforeLast(gameDirectory.Contains("eFootball") ? "\\pak" : "\\Content").SubstringAfterLast("\\");
Provider = InternalGameName switch
var project = gameDirectory.SubstringBeforeLast(gameDirectory.Contains("eFootball") ? "\\pak" : "\\Content").SubstringAfterLast("\\");
Provider = project switch
{
"StateOfDecay2" => new DefaultFileProvider(new DirectoryInfo(gameDirectory),
[
@ -299,11 +281,10 @@ public class CUE4ParseViewModel : ViewModel
{
Provider.SubmitKeys(aesKeys);
Provider.PostMount();
InternalGameName = Provider.InternalGameName;
var aesMax = Provider.RequiredKeys.Count + Provider.Keys.Count;
var archiveMax = Provider.UnloadedVfs.Count + Provider.MountedVfs.Count;
Log.Information($"Project: {InternalGameName} | Mounted: {Provider.MountedVfs.Count}/{archiveMax} | AES: {Provider.Keys.Count}/{aesMax}");
Log.Information($"Project: {Provider.ProjectName} | Mounted: {Provider.MountedVfs.Count}/{archiveMax} | AES: {Provider.Keys.Count}/{aesMax}");
}
public void ClearProvider()
@ -337,7 +318,7 @@ public class CUE4ParseViewModel : ViewModel
{
await _threadWorkerView.Begin(cancellationToken =>
{
var info = _apiEndpointView.FModelApi.GetNews(cancellationToken, Provider.InternalGameName);
var info = _apiEndpointView.FModelApi.GetNews(cancellationToken, Provider.ProjectName);
if (info == null) return;
FLogger.Append(ELog.None, () =>
@ -420,7 +401,7 @@ public class CUE4ParseViewModel : ViewModel
public Task VerifyOnDemandArchives()
{
// only local fortnite
if (Provider is not DefaultFileProvider || !Provider.InternalGameName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase))
if (Provider is not DefaultFileProvider || !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase))
return Task.CompletedTask;
// scuffed but working
@ -477,7 +458,7 @@ public class CUE4ParseViewModel : ViewModel
}
private Task LoadHotfixedLocalizedResources()
{
if (!Provider.InternalGameName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask;
if (!Provider.ProjectName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask;
return Task.Run(() =>
{
var hotfixes = ApplicationService.ApiEndpointView.CentralApi.GetHotfixes(default, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage));
@ -524,7 +505,7 @@ public class CUE4ParseViewModel : ViewModel
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs);
Extract(cancellationToken, asset, TabControl.HasNoTabs);
}
}
@ -552,48 +533,44 @@ public class CUE4ParseViewModel : ViewModel
Parallel.ForEach(folder.AssetsList.Assets, asset =>
{
cancellationToken.ThrowIfCancellationRequested();
ExportData(asset.FullPath, false);
ExportData(asset, false);
});
foreach (var f in folder.Folders) ExportFolder(cancellationToken, f);
}
public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs));
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs));
public void SaveFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto));
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto));
public void TextureFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto));
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto));
public void ModelFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Meshes | EBulkType.Auto));
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Meshes | EBulkType.Auto));
public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto));
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto));
public void Extract(CancellationToken cancellationToken, string fullPath, bool addNewTab = false, EBulkType bulk = EBulkType.None)
public void Extract(CancellationToken cancellationToken, AssetItem asset, bool addNewTab = false, EBulkType bulk = EBulkType.None)
{
Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", fullPath);
Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", asset.FullPath);
var directory = fullPath.SubstringBeforeLast('/');
var fileName = fullPath.SubstringAfterLast('/');
var ext = fullPath.SubstringAfterLast('.').ToLower();
if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(fileName, directory);
else TabControl.SelectedTab.SoftReset(fileName, directory);
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(ext);
if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(asset);
else TabControl.SelectedTab.SoftReset(asset);
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(asset.Extension);
var updateUi = !HasFlag(bulk, EBulkType.Auto);
var saveProperties = HasFlag(bulk, EBulkType.Properties);
var saveTextures = HasFlag(bulk, EBulkType.Textures);
switch (ext)
switch (asset.Extension)
{
case "uasset":
case "umap":
{
var pkg = Provider.LoadPackage(fullPath);
var pkg = Provider.LoadPackage(asset.FullPath, asset.Archive);
if (saveProperties || updateUi)
{
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(pkg.GetExports(), Formatting.Indented), saveProperties, updateUi);
@ -637,85 +614,71 @@ public class CUE4ParseViewModel : ViewModel
case "po":
case "h":
{
if (Provider.TrySaveAsset(fullPath, out var data))
{
using var stream = new MemoryStream(data) { Position = 0 };
using var reader = new StreamReader(stream);
var data = Provider.SaveAsset(asset.FullPath, asset.Archive);
using var stream = new MemoryStream(data) { Position = 0 };
using var reader = new StreamReader(stream);
TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi);
}
TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi);
break;
}
case "locmeta":
{
if (Provider.TryCreateReader(fullPath, out var archive))
{
var metadata = new FTextLocalizationMetaDataResource(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), saveProperties, updateUi);
}
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var metadata = new FTextLocalizationMetaDataResource(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), saveProperties, updateUi);
break;
}
case "locres":
{
if (Provider.TryCreateReader(fullPath, out var archive))
{
var locres = new FTextLocalizationResource(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), saveProperties, updateUi);
}
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var locres = new FTextLocalizationResource(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), saveProperties, updateUi);
break;
}
case "bin" when fileName.Contains("AssetRegistry", StringComparison.OrdinalIgnoreCase):
case "bin" when asset.FileName.Contains("AssetRegistry", StringComparison.OrdinalIgnoreCase):
{
if (Provider.TryCreateReader(fullPath, out var archive))
{
var registry = new FAssetRegistryState(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi);
}
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var registry = new FAssetRegistryState(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi);
break;
}
case "bin" when fileName.Contains("GlobalShaderCache", StringComparison.OrdinalIgnoreCase):
case "bin" when asset.FileName.Contains("GlobalShaderCache", StringComparison.OrdinalIgnoreCase):
{
if (Provider.TryCreateReader(fullPath, out var archive))
{
var registry = new FGlobalShaderCache(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi);
}
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var registry = new FGlobalShaderCache(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi);
break;
}
case "bnk":
case "pck":
{
if (Provider.TryCreateReader(fullPath, out var archive))
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var wwise = new WwiseReader(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi);
foreach (var (name, data) in wwise.WwiseEncodedMedias)
{
var wwise = new WwiseReader(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi);
foreach (var (name, data) in wwise.WwiseEncodedMedias)
{
SaveAndPlaySound(fullPath.SubstringBeforeWithLast("/") + name, "WEM", data);
}
SaveAndPlaySound(asset.FullPath.SubstringBeforeWithLast('/') + name, "WEM", data);
}
break;
}
case "wem":
{
if (Provider.TrySaveAsset(fullPath, out var input))
SaveAndPlaySound(fullPath, "WEM", input);
var data = Provider.SaveAsset(asset.FullPath, asset.Archive);
SaveAndPlaySound(asset.FullPath, "WEM", data);
break;
}
case "udic":
{
if (Provider.TryCreateReader(fullPath, out var archive))
{
var header = new FOodleDictionaryArchive(archive).Header;
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), saveProperties, updateUi);
}
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var header = new FOodleDictionaryArchive(archive).Header;
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), saveProperties, updateUi);
break;
}
@ -723,55 +686,49 @@ public class CUE4ParseViewModel : ViewModel
case "jpg":
case "bmp":
{
if (Provider.TrySaveAsset(fullPath, out var data))
{
using var stream = new MemoryStream(data) { Position = 0 };
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, SKBitmap.Decode(stream), saveTextures, updateUi);
}
var data = Provider.SaveAsset(asset.FullPath, asset.Archive);
using var stream = new MemoryStream(data) { Position = 0 };
TabControl.SelectedTab.AddImage(asset.FileName.SubstringBeforeLast("."), false, SKBitmap.Decode(stream), saveTextures, updateUi);
break;
}
case "svg":
{
if (Provider.TrySaveAsset(fullPath, out var data))
var data = Provider.SaveAsset(asset.FullPath, asset.Archive);
using var stream = new MemoryStream(data) { Position = 0 };
var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512));
svg.Load(stream);
var bitmap = new SKBitmap(512, 512);
using (var canvas = new SKCanvas(bitmap))
using (var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium })
{
using var stream = new MemoryStream(data) { Position = 0 };
var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512));
svg.Load(stream);
var bitmap = new SKBitmap(512, 512);
using (var canvas = new SKCanvas(bitmap))
using (var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium })
{
canvas.DrawPicture(svg.Picture, paint);
}
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, bitmap, saveTextures, updateUi);
canvas.DrawPicture(svg.Picture, paint);
}
TabControl.SelectedTab.AddImage(asset.FileName.SubstringBeforeLast("."), false, bitmap, saveTextures, updateUi);
break;
}
case "ufont":
case "otf":
case "ttf":
FLogger.Append(ELog.Warning, () =>
FLogger.Text($"Export '{fileName}' raw data and change its extension if you want it to be an installable font file", Constants.WHITE, true));
FLogger.Text($"Export '{asset.FileName}' raw data and change its extension if you want it to be an installable font file", Constants.WHITE, true));
break;
case "ushaderbytecode":
case "ushadercode":
{
if (Provider.TryCreateReader(fullPath, out var archive))
{
var ar = new FShaderCodeArchive(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi);
}
var archive = Provider.CreateReader(asset.FullPath, asset.Archive);
var ar = new FShaderCodeArchive(archive);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi);
break;
}
default:
{
FLogger.Append(ELog.Warning, () =>
FLogger.Text($"The package '{fileName}' is of an unknown type.", Constants.WHITE, true));
FLogger.Text($"The package '{asset.FileName}' is of an unknown type.", Constants.WHITE, true));
break;
}
}
@ -780,7 +737,7 @@ public class CUE4ParseViewModel : ViewModel
public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName, string parentExportType)
{
Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath);
TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/'), parentExportType);
TabControl.AddTab(new AssetItem(fullPath), parentExportType);
TabControl.SelectedTab.ScrollTrigger = objectName;
var pkg = Provider.LoadPackage(fullPath);
@ -840,7 +797,7 @@ public class CUE4ParseViewModel : ViewModel
{
var fileName = sourceFile.SubstringAfterLast('/');
var path = Path.Combine(UserSettings.Default.TextureDirectory,
UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Directory : "", fileName!).Replace('\\', '/');
UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Asset.Directory : "", fileName!).Replace('\\', '/');
Directory.CreateDirectory(path.SubstringBeforeLast('/'));
@ -881,7 +838,7 @@ public class CUE4ParseViewModel : ViewModel
return false;
}
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Directory, TabControl.SelectedTab.Header.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
SaveAndPlaySound(Path.Combine(TabControl.SelectedTab.Asset.FullPath.SubstringBeforeLast('.')).Replace('\\', '/'), audioFormat, data);
return false;
}
case UWorld when isNone && UserSettings.Default.PreviewWorlds:
@ -897,7 +854,7 @@ public class CUE4ParseViewModel : ViewModel
case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes:
case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh:
case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial &&
!(Provider.InternalGameName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) &&
!(Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) &&
(pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) ||
pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) ||
pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))):
@ -943,15 +900,13 @@ public class CUE4ParseViewModel : ViewModel
}
}
public void ShowMetadata(string fullPath)
public void ShowMetadata(AssetItem asset)
{
var package = Provider.LoadPackage(fullPath);
var package = Provider.LoadPackage(asset.FullPath, asset.Archive);
var directory = fullPath.SubstringBeforeLast('/');
var fileName = $"{fullPath.SubstringAfterLast('/')} (Metadata)";
if (TabControl.CanAddTabs) TabControl.AddTab(fileName, directory);
else TabControl.SelectedTab.SoftReset(fileName, directory);
var a = new AssetItem(" (Metadata)", asset);
if (TabControl.CanAddTabs) TabControl.AddTab(a);
else TabControl.SelectedTab.SoftReset(a);
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("");
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(package, Formatting.Indented), false, false);
@ -1008,10 +963,11 @@ public class CUE4ParseViewModel : ViewModel
}
private readonly object _rawData = new ();
public void ExportData(string fullPath, bool updateUi = true)
public void ExportData(AssetItem asset, bool updateUi = true)
{
var fileName = fullPath.SubstringAfterLast('/');
if (Provider.TrySavePackage(fullPath, out var assets))
// TODO: export by archive
// is that even useful? if user doesn't rename manually it's gonna overwrite the file anyway
if (Provider.TrySavePackage(asset.FullPath, out var assets))
{
string path = UserSettings.Default.RawDataDirectory;
Parallel.ForEach(assets, kvp =>
@ -1024,21 +980,21 @@ public class CUE4ParseViewModel : ViewModel
}
});
Log.Information("{FileName} successfully exported", fileName);
Log.Information("{FileName} successfully exported", asset.FileName);
if (updateUi)
{
FLogger.Append(ELog.Information, () =>
{
FLogger.Text("Successfully exported ", Constants.WHITE);
FLogger.Link(fileName, path, true);
FLogger.Link(asset.FileName, path, true);
});
}
}
else
{
Log.Error("{FileName} could not be exported", fileName);
Log.Error("{FileName} could not be exported", asset.FileName);
if (updateUi)
FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{fileName}'", Constants.WHITE, true));
FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{asset.FileName}'", Constants.WHITE, true));
}
}

View File

@ -2,7 +2,7 @@
using System.Linq;
using System.Text;
using System.Windows;
using FModel.Extensions;
using CUE4Parse.Utils;
using FModel.Framework;
namespace FModel.ViewModels.Commands;
@ -43,4 +43,4 @@ public class CopyCommand : ViewModelCommand<ApplicationViewModel>
Clipboard.SetText(sb.ToString().TrimEnd());
}
}
}

View File

@ -10,7 +10,7 @@ using System.Threading.Tasks;
using AdonisUI.Controls;
using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.VirtualFileSystem;
using FModel.Creator;
using CUE4Parse.Utils;
using FModel.Extensions;
using FModel.Framework;
using FModel.Services;

View File

@ -29,7 +29,7 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
Helper.OpenWindow<AdonisWindow>("AES Manager", () => new AesManager().Show());
break;
case "Directory_Backup":
Helper.OpenWindow<AdonisWindow>("Backup Manager", () => new BackupManager(contextViewModel.CUE4Parse.Provider.InternalGameName).Show());
Helper.OpenWindow<AdonisWindow>("Backup Manager", () => new BackupManager(contextViewModel.CUE4Parse.Provider.ProjectName).Show());
break;
case "Directory_ArchivesInfo":
contextViewModel.CUE4Parse.TabControl.AddTab("Archives Info");

View File

@ -32,7 +32,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, true);
contextViewModel.CUE4Parse.Extract(cancellationToken, asset, true);
}
break;
case "Assets_Show_Metadata":
@ -40,7 +40,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.ShowMetadata(asset.FullPath);
contextViewModel.CUE4Parse.ShowMetadata(asset);
}
break;
case "Assets_Export_Data":
@ -48,7 +48,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.ExportData(asset.FullPath);
contextViewModel.CUE4Parse.ExportData(asset);
}
break;
case "Assets_Save_Properties":
@ -56,7 +56,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Properties | updateUi);
contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Properties | updateUi);
}
break;
case "Assets_Save_Textures":
@ -64,7 +64,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Textures | updateUi);
contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Textures | updateUi);
}
break;
case "Assets_Save_Models":
@ -72,7 +72,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Meshes | updateUi);
contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Meshes | updateUi);
}
break;
case "Assets_Save_Animations":
@ -80,7 +80,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
{
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
contextViewModel.CUE4Parse.Extract(cancellationToken, asset.FullPath, false, EBulkType.Animations | updateUi);
contextViewModel.CUE4Parse.Extract(cancellationToken, asset, false, EBulkType.Animations | updateUi);
}
break;
}

View File

@ -32,44 +32,44 @@ public class TabCommand : ViewModelCommand<TabItem>
_applicationView.CUE4Parse.TabControl.RemoveOtherTabs(contextViewModel);
break;
case "Asset_Export_Data":
await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(contextViewModel.FullPath));
await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(contextViewModel.Asset));
break;
case "Asset_Save_Properties":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Properties);
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Properties);
});
break;
case "Asset_Save_Textures":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Textures);
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Textures);
});
break;
case "Asset_Save_Models":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Meshes | EBulkType.Auto);
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Meshes);
});
break;
case "Asset_Save_Animations":
await _threadWorkerView.Begin(cancellationToken =>
{
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.FullPath, false, EBulkType.Animations | EBulkType.Auto);
_applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Asset, false, EBulkType.Animations);
});
break;
case "Open_Properties":
if (contextViewModel.Header == "New Tab" || contextViewModel.Document == null) return;
Helper.OpenWindow<AdonisWindow>(contextViewModel.Header + " (Properties)", () =>
if (contextViewModel.Asset.FileName == "New Tab" || contextViewModel.Document == null) return;
Helper.OpenWindow<AdonisWindow>(contextViewModel.Asset.FileName + " (Properties)", () =>
{
new PropertiesPopout(contextViewModel)
{
Title = contextViewModel.Header + " (Properties)"
Title = contextViewModel.Asset.FileName + " (Properties)"
}.Show();
});
break;
case "Copy_Asset_Path":
Clipboard.SetText(contextViewModel.FullPath);
Clipboard.SetText(contextViewModel.Asset.FullPath);
break;
}
}

View File

@ -1,4 +1,3 @@
using FModel.Extensions;
using FModel.Framework;
using Newtonsoft.Json;
using Serilog;
@ -10,6 +9,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using CUE4Parse.UE4.Objects.Core.Serialization;
using CUE4Parse.UE4.Versions;
using CUE4Parse.Utils;
using FModel.Settings;
using FModel.ViewModels.ApiEndpoints.Models;
using Microsoft.Win32;

View File

@ -16,6 +16,7 @@ using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.Utils;
namespace FModel.ViewModels;
@ -92,22 +93,13 @@ public class TabItem : ViewModel
{
public string ParentExportType { get; private set; }
private string _header;
public string Header
private AssetItem _asset;
public AssetItem Asset
{
get => _header;
set => SetProperty(ref _header, value);
get => _asset;
set => SetProperty(ref _asset, value);
}
private string _directory;
public string Directory
{
get => _directory;
set => SetProperty(ref _directory, value);
}
public string FullPath => this.Directory + "/" + this.Header.SubstringBeforeLast(" (");
private bool _hasSearchOpen;
public bool HasSearchOpen
{
@ -217,18 +209,16 @@ public class TabItem : ViewModel
private GoToCommand _goToCommand;
public GoToCommand GoToCommand => _goToCommand ??= new GoToCommand(null);
public TabItem(string header, string directory, string parentExportType)
public TabItem(AssetItem asset, string parentExportType)
{
Header = header;
Directory = directory;
Asset = asset;
ParentExportType = parentExportType;
_images = new ObservableCollection<TabImage>();
}
public void SoftReset(string header, string directory)
public void SoftReset(AssetItem asset)
{
Header = header;
Directory = directory;
Asset = asset;
ParentExportType = string.Empty;
ScrollTrigger = null;
Application.Current.Dispatcher.Invoke(() =>
@ -316,9 +306,9 @@ public class TabItem : ViewModel
var fileName = image.ExportName + ext;
var path = Path.Combine(UserSettings.Default.TextureDirectory,
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName!).Replace('\\', '/');
UserSettings.Default.KeepDirectoryStructure ? Asset.Directory : "", fileName!).Replace('\\', '/');
System.IO.Directory.CreateDirectory(path.SubstringBeforeLast('/'));
Directory.CreateDirectory(path.SubstringBeforeLast('/'));
SaveImage(image, path, fileName, updateUi);
}
@ -337,11 +327,11 @@ public class TabItem : ViewModel
public void SaveProperty(bool updateUi)
{
var fileName = Path.ChangeExtension(Header, ".json");
var fileName = Path.ChangeExtension(Asset.FileName, ".json");
var directory = Path.Combine(UserSettings.Default.PropertiesDirectory,
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName).Replace('\\', '/');
UserSettings.Default.KeepDirectoryStructure ? Asset.Directory : "", fileName).Replace('\\', '/');
System.IO.Directory.CreateDirectory(directory.SubstringBeforeLast('/'));
Directory.CreateDirectory(directory.SubstringBeforeLast('/'));
Application.Current.Dispatcher.Invoke(() => File.WriteAllText(directory, Document.Text));
SaveCheck(directory, fileName, updateUi);
@ -390,28 +380,27 @@ public class TabControlViewModel : ViewModel
public TabControlViewModel()
{
_tabItems = new ObservableCollection<TabItem>(EnumerateTabs());
_tabItems = [];
TabsItems = new ReadOnlyObservableCollection<TabItem>(_tabItems);
SelectedTab = TabsItems.FirstOrDefault();
AddTab();
}
public void AddTab(string header = null, string directory = null, string parentExportType = null)
public void AddTab() => AddTab("New Tab");
public void AddTab(string title) => AddTab(new AssetItem(title));
public void AddTab(AssetItem asset, string parentExportType = null)
{
if (!CanAddTabs) return;
var h = header ?? "New Tab";
var d = directory ?? string.Empty;
var p = parentExportType ?? string.Empty;
if (SelectedTab is { Header : "New Tab" })
if (SelectedTab?.Asset.FileName == "New Tab")
{
SelectedTab.Header = h;
SelectedTab.Directory = d;
SelectedTab.Asset = asset;
return;
}
Application.Current.Dispatcher.Invoke(() =>
{
_tabItems.Add(new TabItem(h, d, p));
_tabItems.Add(new TabItem(asset, p));
SelectedTab = _tabItems.Last();
});
}
@ -470,9 +459,4 @@ public class TabControlViewModel : ViewModel
_tabItems.Clear();
});
}
private static IEnumerable<TabItem> EnumerateTabs()
{
yield return new TabItem("New Tab", string.Empty, string.Empty);
}
}

View File

@ -3,7 +3,7 @@ using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using FModel.Extensions;
using CUE4Parse.Utils;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;

View File

@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using CUE4Parse.Utils;
using FModel.Extensions;
using FModel.Services;
using FModel.ViewModels;

View File

@ -119,7 +119,7 @@ public partial class AvalonEditor
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 + '/' + StringExtensions.SubstringBeforeLast(tabItem.Header, '.');
avalonEditor.Document.FileName = tabItem.Asset.FullPath.SubstringBeforeLast('.');
if (!_savedCarets.ContainsKey(avalonEditor.Document.FileName))
_ignoreCaret = true;

View File

@ -2,7 +2,7 @@
using System.Text.RegularExpressions;
using System.Windows.Input;
using System.Windows.Media;
using FModel.Extensions;
using CUE4Parse.Utils;
using FModel.ViewModels;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
@ -24,7 +24,7 @@ public partial class PropertiesPopout
MyAvalonEditor.Document = new TextDocument
{
Text = contextViewModel.Document.Text,
FileName = contextViewModel.Directory + '/' + contextViewModel.Header.SubstringBeforeLast('.')
FileName = contextViewModel.Asset.FullPath.SubstringBeforeLast('.')
};
MyAvalonEditor.FontSize = contextViewModel.FontSize;
MyAvalonEditor.SyntaxHighlighting = contextViewModel.Highlighter;

View File

@ -1,7 +1,7 @@
using System;
using System.Globalization;
using System.Windows.Data;
using FModel.Extensions;
using CUE4Parse.Utils;
namespace FModel.Views.Resources.Converters;
@ -18,4 +18,4 @@ public class FullPathToFileConverter : IValueConverter
{
throw new NotImplementedException();
}
}
}

View File

@ -651,7 +651,7 @@
<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 DockPanel.Dock="Left" Text="{Binding Asset.FileName}" TextTrimming="CharacterEllipsis" ToolTip="{Binding Asset.FileName}">
<TextBlock.Width>
<MultiBinding Converter="{x:Static converters:TabSizeConverter.Instance}" ConverterParameter="6">
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}" />
@ -913,7 +913,7 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Jump to Package Folder" Command="{Binding GoToCommand}" CommandParameter="{Binding Directory}">
<MenuItem Header="Jump to Package Folder" Command="{Binding GoToCommand}" CommandParameter="{Binding Asset.Directory}">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using FModel.Extensions;
using FModel.Services;
using FModel.ViewModels;
@ -36,7 +35,7 @@ public partial class SearchView
WindowState = WindowState.Minimized;
MainWindow.YesWeCats.AssetsListName.ItemsSource = null;
var folder = _applicationView.CustomDirectories.GoToCommand.JumpTo(assetItem.FullPath.SubstringBeforeLast('/'));
var folder = _applicationView.CustomDirectories.GoToCommand.JumpTo(assetItem.Directory);
if (folder == null) return;
MainWindow.YesWeCats.Activate();
@ -58,7 +57,7 @@ public partial class SearchView
return;
WindowState = WindowState.Minimized;
await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.Extract(cancellationToken, assetItem.FullPath, true));
await _threadWorkerView.Begin(cancellationToken => _applicationView.CUE4Parse.Extract(cancellationToken, assetItem, true));
MainWindow.YesWeCats.Activate();
}

View File

@ -534,7 +534,7 @@
<Style TargetType="TreeViewItem" BasedOn="{StaticResource TreeViewItemStyle}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding CUE4Parse.InternalGameName, Converter={x:Static converters:CaseInsensitiveStringEqualsConverter.Instance}, ConverterParameter='FortniteGame'}" Value="True">
<DataTrigger Binding="{Binding CUE4Parse.Provider.ProjectName, Converter={x:Static converters:CaseInsensitiveStringEqualsConverter.Instance}, ConverterParameter='FortniteGame'}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>

View File

@ -64,7 +64,7 @@ public class Options
["tl_next"] = new ("tl_next"),
};
_game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.InternalGameName.ToUpper();
_game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.ProjectName.ToUpper();
SelectModel(Guid.Empty);
}

View File

@ -18,8 +18,8 @@ using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.Core.Misc;
using CUE4Parse.UE4.Objects.Engine;
using CUE4Parse.UE4.Objects.UObject;
using CUE4Parse.Utils;
using FModel.Creator;
using FModel.Extensions;
using FModel.Settings;
using FModel.Views.Snooper.Animations;
using FModel.Views.Snooper.Buffers;
@ -430,7 +430,7 @@ public class Renderer : IDisposable
return;
if (persistentLevel.TryGetValue(out FSoftObjectPath runtimeCell, "WorldPartitionRuntimeCell") &&
Utils.TryLoadObject(runtimeCell.AssetPathName.Text.SubstringBeforeWithLast(".") + runtimeCell.SubPathString.SubstringAfterLast("."), out UObject worldPartition))
runtimeCell.TryLoad(out UObject worldPartition))
{
var position = worldPartition.GetOrDefault("Position", FVector.ZeroVector) * Constants.SCALE_DOWN_RATIO;
var box = worldPartition.GetOrDefault("ContentBounds", new FBox(FVector.ZeroVector, FVector.OneVector));