mirror of
https://github.com/4sval/FModel.git
synced 2026-04-26 08:13:27 -05:00
added links to some exceptions + auto detect ue version (#657)
Some checks failed
FModel QA Builder / build (push) Has been cancelled
Some checks failed
FModel QA Builder / build (push) Has been cancelled
Co-authored-by: Asval <asval.contactme@gmail.com> Co-authored-by: Krowe-moh <27891447+Krowe-moh@users.noreply.github.com>
This commit is contained in:
parent
c9542f1a91
commit
639f21e574
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7ed9bd5adf3daada4bd7d884f3a42d163a64247a
|
Subproject commit 6d7157a29b08d583aef9887a56d70f27a2ff36d5
|
||||||
|
|
@ -40,6 +40,12 @@ public static class Constants
|
||||||
|
|
||||||
public const string _NO_PRESET_TRIGGER = "Hand Made";
|
public const string _NO_PRESET_TRIGGER = "Hand Made";
|
||||||
|
|
||||||
|
// Common issues
|
||||||
|
public const string MAPPING_ISSUE_LINK = "https://github.com/4sval/FModel/discussions/418";
|
||||||
|
public const string AUDIO_ISSUE_LINK = "https://github.com/4sval/FModel/discussions/658";
|
||||||
|
public const string RADA_ISSUE_LINK = "https://github.com/4sval/FModel/discussions/422";
|
||||||
|
public const string VERSION_ISSUE_LINK = "https://github.com/4sval/FModel/discussions/425";
|
||||||
|
|
||||||
public static int PALETTE_LENGTH => COLOR_PALETTE.Length;
|
public static int PALETTE_LENGTH => COLOR_PALETTE.Length;
|
||||||
public static readonly Vector3[] COLOR_PALETTE =
|
public static readonly Vector3[] COLOR_PALETTE =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -252,13 +252,6 @@ namespace FModel.Settings
|
||||||
set => SetProperty(ref _imageMergerMargin, value);
|
set => SetProperty(ref _imageMergerMargin, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _canExportRawData;
|
|
||||||
public bool CanExportRawData
|
|
||||||
{
|
|
||||||
get => _canExportRawData;
|
|
||||||
set => SetProperty(ref _canExportRawData, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _readScriptData;
|
private bool _readScriptData;
|
||||||
public bool ReadScriptData
|
public bool ReadScriptData
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ public class ApplicationViewModel : ViewModel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FLogger.Append(ELog.Error, () => FLogger.Text("Could not download VgmStream", Constants.WHITE, true));
|
FLogger.Append(ELog.Error, () => FLogger.Text("Could not download vgmstream", Constants.WHITE, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,13 +298,23 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
||||||
Save(a, true);
|
Save(a, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
FLogger.Append(ELog.Information, () =>
|
|
||||||
{
|
|
||||||
FLogger.Text("Successfully saved audio from ", Constants.WHITE);
|
|
||||||
FLogger.Link(_audioFiles.First().FileName, _audioFiles.First().FilePath, true);
|
|
||||||
});
|
|
||||||
if (_audioFiles.Count > 1)
|
if (_audioFiles.Count > 1)
|
||||||
FLogger.Append(ELog.Information, () => FLogger.Text($"Successfully saved {_audioFiles.Count} audio files", Constants.WHITE, true));
|
{
|
||||||
|
var dir = new DirectoryInfo(Path.GetDirectoryName(_audioFiles.First().FilePath));
|
||||||
|
FLogger.Append(ELog.Information, () =>
|
||||||
|
{
|
||||||
|
FLogger.Text($"Successfully saved {_audioFiles.Count} audio files to ", Constants.WHITE);
|
||||||
|
FLogger.Link(dir.Name, dir.FullName, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FLogger.Append(ELog.Information, () =>
|
||||||
|
{
|
||||||
|
FLogger.Text("Successfully saved ", Constants.WHITE);
|
||||||
|
FLogger.Link(_audioFiles.First().FileName, _audioFiles.First().FilePath, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,15 +664,24 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryConvert(out string wavFilePath) => TryConvert(SelectedAudioFile.FilePath, SelectedAudioFile.Data, out wavFilePath);
|
private bool TryConvert(out string wavFilePath) => TryConvert(SelectedAudioFile.FilePath, SelectedAudioFile.Data, out wavFilePath, true);
|
||||||
public static bool TryConvert(string inputFilePath, byte[] inputFileData, out string wavFilePath)
|
public static bool TryConvert(string inputFilePath, byte[] inputFileData, out string wavFilePath, bool updateUi = false)
|
||||||
{
|
{
|
||||||
wavFilePath = string.Empty;
|
wavFilePath = string.Empty;
|
||||||
var vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "test.exe");
|
var vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "test.exe");
|
||||||
if (!File.Exists(vgmFilePath))
|
if (!File.Exists(vgmFilePath))
|
||||||
{
|
{
|
||||||
vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-cli.exe");
|
vgmFilePath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", "vgmstream-cli.exe");
|
||||||
if (!File.Exists(vgmFilePath)) return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(inputFilePath.SubstringBeforeLast("/"));
|
Directory.CreateDirectory(inputFilePath.SubstringBeforeLast("/"));
|
||||||
|
|
@ -679,7 +698,22 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
||||||
vgmProcess?.WaitForExit(5000);
|
vgmProcess?.WaitForExit(5000);
|
||||||
|
|
||||||
File.Delete(inputFilePath);
|
File.Delete(inputFilePath);
|
||||||
return vgmProcess?.ExitCode == 0 && File.Exists(wavFilePath);
|
|
||||||
|
var success = vgmProcess?.ExitCode == 0 && File.Exists(wavFilePath);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to convert {InputFilePath} to .wav format", inputFilePath);
|
||||||
|
if (updateUi)
|
||||||
|
{
|
||||||
|
FLogger.Append(ELog.Error, () =>
|
||||||
|
{
|
||||||
|
FLogger.Text("Failed to convert audio to .wav format. See: ", Constants.WHITE);
|
||||||
|
FLogger.Link("→ link ←", Constants.AUDIO_ISSUE_LINK, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryDecode(string extension, out string rawFilePath)
|
private bool TryDecode(string extension, out string rawFilePath)
|
||||||
|
|
@ -688,6 +722,12 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
||||||
var decoderPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", $"{extension}dec.exe");
|
var decoderPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", $"{extension}dec.exe");
|
||||||
if (!File.Exists(decoderPath))
|
if (!File.Exists(decoderPath))
|
||||||
{
|
{
|
||||||
|
Log.Error("Failed to convert {FilePath}, rada decoder is missing", SelectedAudioFile.FilePath);
|
||||||
|
FLogger.Append(ELog.Error, () =>
|
||||||
|
{
|
||||||
|
FLogger.Text("Failed to convert audio because rada decoder is missing. See: ", Constants.WHITE);
|
||||||
|
FLogger.Link("→ link ←", Constants.RADA_ISSUE_LINK, true);
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1179,7 +1179,7 @@ public class CUE4ParseViewModel : ViewModel
|
||||||
case USoundWave when isNone || saveAudio:
|
case USoundWave when isNone || saveAudio:
|
||||||
{
|
{
|
||||||
// If UAkMediaAsset exists in the same package it should be used to handle the audio instead (because it contains actual audio name)
|
// If UAkMediaAsset exists in the same package it should be used to handle the audio instead (because it contains actual audio name)
|
||||||
if (pointer.Object.Value is UAkMediaAssetData dataObj && dataObj.Outer is UAkMediaAsset)
|
if (pointer.Object.Value is UAkMediaAssetData dataObj && dataObj.Outer.Object.Value is UAkMediaAsset)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed;
|
var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed;
|
||||||
|
|
@ -1196,7 +1196,7 @@ public class CUE4ParseViewModel : ViewModel
|
||||||
}
|
}
|
||||||
case UAkMediaAsset when (isNone || saveAudio) && pointer.Object.Value is UAkMediaAsset akMediaAsset:
|
case UAkMediaAsset when (isNone || saveAudio) && pointer.Object.Value is UAkMediaAsset akMediaAsset:
|
||||||
{
|
{
|
||||||
var audioName = akMediaAsset.MediaName;
|
var audioName = akMediaAsset.MediaName ?? akMediaAsset.Name;
|
||||||
if (akMediaAsset.CurrentMediaAssetData?.TryLoad<UAkMediaAssetData>(out var akMediaAssetData) is true)
|
if (akMediaAsset.CurrentMediaAssetData?.TryLoad<UAkMediaAssetData>(out var akMediaAssetData) is true)
|
||||||
{
|
{
|
||||||
var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed;
|
var shouldDecompress = UserSettings.Default.CompressedAudioMode is ECompressedAudio.PlayDecompressed;
|
||||||
|
|
@ -1416,25 +1416,15 @@ public class CUE4ParseViewModel : ViewModel
|
||||||
writer.Flush();
|
writer.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool conversionSuccess = true;
|
||||||
if (UserSettings.Default.ConvertAudioOnBulkExport)
|
if (UserSettings.Default.ConvertAudioOnBulkExport)
|
||||||
{
|
{
|
||||||
AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath);
|
conversionSuccess = AudioPlayerViewModel.TryConvert(savedAudioPath, data, out string wavFilePath);
|
||||||
if (!string.IsNullOrEmpty(wavFilePath))
|
if (conversionSuccess) savedAudioPath = wavFilePath;
|
||||||
{
|
|
||||||
savedAudioPath = wavFilePath;
|
|
||||||
}
|
|
||||||
else if (updateUi)
|
|
||||||
{
|
|
||||||
FLogger.Append(ELog.Error, () =>
|
|
||||||
{
|
|
||||||
FLogger.Text("Failed to convert audio to WAV format, aborting extraction.", Constants.WHITE, true);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Information("Successfully saved {FilePath}", savedAudioPath);
|
Log.Information("Successfully saved {FilePath}", savedAudioPath);
|
||||||
if (updateUi)
|
if (updateUi && conversionSuccess)
|
||||||
{
|
{
|
||||||
FLogger.Append(ELog.Information, () =>
|
FLogger.Append(ELog.Information, () =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ public class GameFileViewModel(GameFile asset) : ViewModel
|
||||||
case "csv":
|
case "csv":
|
||||||
AssetCategory = EAssetCategory.Data;
|
AssetCategory = EAssetCategory.Data;
|
||||||
break;
|
break;
|
||||||
|
case "stinfo":
|
||||||
case "ushaderbytecode":
|
case "ushaderbytecode":
|
||||||
AssetCategory = EAssetCategory.ByteCode;
|
AssetCategory = EAssetCategory.ByteCode;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ using Serilog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
@ -67,12 +69,103 @@ public class GameSelectorViewModel : ViewModel
|
||||||
public void AddUndetectedDir(string gameDirectory) => AddUndetectedDir(gameDirectory.SubstringAfterLast('\\'), gameDirectory);
|
public void AddUndetectedDir(string gameDirectory) => AddUndetectedDir(gameDirectory.SubstringAfterLast('\\'), gameDirectory);
|
||||||
public void AddUndetectedDir(string gameName, string gameDirectory)
|
public void AddUndetectedDir(string gameName, string gameDirectory)
|
||||||
{
|
{
|
||||||
var setting = DirectorySettings.Default(gameName, gameDirectory, true);
|
if (TryDetectUeVersion(gameDirectory, out var ueVersion, out var newGameDirectory))
|
||||||
|
{
|
||||||
|
// gameDirectory = newGameDirectory; // directory was changed to point to the correct paks folder
|
||||||
|
}
|
||||||
|
|
||||||
|
var setting = DirectorySettings.Default(gameName, gameDirectory, true, ueVersion);
|
||||||
UserSettings.Default.PerDirectory[gameDirectory] = setting;
|
UserSettings.Default.PerDirectory[gameDirectory] = setting;
|
||||||
_detectedDirectories.Add(setting);
|
_detectedDirectories.Add(setting);
|
||||||
SelectedDirectory = DetectedDirectories.Last();
|
SelectedDirectory = DetectedDirectories.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryDetectUeVersion(string gameDirectory, out EGame ueVersion, [MaybeNullWhen(false)] out string newGameDirectory)
|
||||||
|
{
|
||||||
|
var targetGameDir = gameDirectory;
|
||||||
|
if (!targetGameDir.EndsWith("Paks", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var dirs = Directory.GetDirectories(targetGameDir, "Paks", SearchOption.AllDirectories);
|
||||||
|
var paksDir = dirs.Length == 1 ? dirs[0] : dirs.FirstOrDefault(x => !x.EndsWith("Engine\\Programs\\CrashReportClient\\Content\\Paks"));
|
||||||
|
if (!string.IsNullOrEmpty(paksDir))
|
||||||
|
{
|
||||||
|
Log.Warning("Selected directory \"{GameDirectory}\" does not end with \"Paks\". Looking in \"{PaksDir}\" instead.", targetGameDir, paksDir);
|
||||||
|
targetGameDir = paksDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Directory.GetFiles(gameDirectory, "*.exe") is { Length: 1 } exe && TryGetUeVersionFromExe(exe[0], out ueVersion))
|
||||||
|
{
|
||||||
|
// we checked the exe in the original directory, the BootstrapPackagedGame one
|
||||||
|
// but we still want c4p to use the paks folder as the game directory (if any), not the original one
|
||||||
|
newGameDirectory = targetGameDir;
|
||||||
|
Log.Information("Detected UE version {UeVersion} from \"{Exe}\"", ueVersion, exe[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// past this point, we assume targetGameDir is the correct Paks folder
|
||||||
|
newGameDirectory = targetGameDir;
|
||||||
|
var projectDir = Path.Combine(targetGameDir, "..", "..");
|
||||||
|
|
||||||
|
var projectBinariesDir = Path.Combine(projectDir, "Binaries", "Win64");
|
||||||
|
if (Directory.Exists(projectBinariesDir))
|
||||||
|
{
|
||||||
|
if (Directory.GetFiles(projectBinariesDir, "*-Win64-Shipping.exe") is { Length: > 0 } shipping)
|
||||||
|
{
|
||||||
|
foreach (var exe in shipping)
|
||||||
|
{
|
||||||
|
if (TryGetUeVersionFromExe(exe, out ueVersion))
|
||||||
|
{
|
||||||
|
Log.Information("Detected UE version {UeVersion} from \"{Exe}\"", ueVersion, exe);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Directory.GetFiles(projectBinariesDir, "*.exe") is { Length: < 3 } exes)
|
||||||
|
{
|
||||||
|
foreach (var exe in exes)
|
||||||
|
{
|
||||||
|
if (TryGetUeVersionFromExe(exe, out ueVersion))
|
||||||
|
{
|
||||||
|
Log.Information("Detected UE version {UeVersion} from \"{Exe}\"", ueVersion, exe);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var crashReportClientExe = Path.Combine(projectDir, "..", "Engine", "Binaries", "Win64", "CrashReportClient.exe");
|
||||||
|
if (File.Exists(crashReportClientExe) && TryGetUeVersionFromExe(crashReportClientExe, out ueVersion))
|
||||||
|
{
|
||||||
|
Log.Information("Detected UE version {UeVersion} from \"{Exe}\"", ueVersion, crashReportClientExe);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ueVersion = EGame.GAME_UE4_LATEST;
|
||||||
|
Log.Warning("Failed to detect UE version for \"{GameDirectory}\".", gameDirectory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetUeVersionFromExe(string exePath, out EGame ueVersion)
|
||||||
|
{
|
||||||
|
ueVersion = EGame.GAME_UE4_LATEST;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var info = FileVersionInfo.GetVersionInfo(exePath);
|
||||||
|
ueVersion = info.FileMajorPart switch
|
||||||
|
{
|
||||||
|
4 => (EGame) Math.Min((uint)(GameUtils.GameUe4Base + (info.FileMinorPart << 16)), (uint) EGame.GAME_UE4_LATEST),
|
||||||
|
5 => (EGame) Math.Min((uint)(GameUtils.GameUe5Base + (info.FileMinorPart << 16)), (uint) EGame.GAME_UE5_LATEST),
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported UE major version {info.FileMajorPart} detected from {exePath}")
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void DeleteSelectedGame()
|
public void DeleteSelectedGame()
|
||||||
{
|
{
|
||||||
UserSettings.Default.PerDirectory.Remove(SelectedDirectory.GameDirectory); // should not be a problem
|
UserSettings.Default.PerDirectory.Remove(SelectedDirectory.GameDirectory); // should not be a problem
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CUE4Parse.UE4.Exceptions;
|
||||||
using FModel.Framework;
|
using FModel.Framework;
|
||||||
using FModel.Services;
|
using FModel.Services;
|
||||||
using FModel.Views.Resources.Controls;
|
using FModel.Views.Resources.Controls;
|
||||||
|
|
@ -100,7 +101,26 @@ public class ThreadWorkerViewModel : ViewModel
|
||||||
CurrentCancellationTokenSource = null; // kill token
|
CurrentCancellationTokenSource = null; // kill token
|
||||||
|
|
||||||
Log.Error("{Exception}", e);
|
Log.Error("{Exception}", e);
|
||||||
FLogger.Append(e);
|
switch (e)
|
||||||
|
{
|
||||||
|
case MappingException:
|
||||||
|
FLogger.Append(ELog.Error, () =>
|
||||||
|
{
|
||||||
|
FLogger.Text("Package has unversioned properties but mapping file (.usmap) is missing, can't serialize. See: ", Constants.WHITE);
|
||||||
|
FLogger.Link("→ link ←", Constants.MAPPING_ISSUE_LINK, true);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case VersionException v: // Error might be unrelated to version, but it's usually the case
|
||||||
|
FLogger.Append(ELog.Error, () =>
|
||||||
|
{
|
||||||
|
FLogger.Text(v.Message[..^1] + ", can't serialize. Make sure the correct UE version is configured. See: ", Constants.WHITE);
|
||||||
|
FLogger.Link("→ link ←", Constants.VERSION_ISSUE_LINK, true);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FLogger.Append(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,7 @@
|
||||||
</MenuItem.Style>
|
</MenuItem.Style>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Command="{Binding RightClickMenuCommand}"
|
<MenuItem Command="{Binding RightClickMenuCommand}">
|
||||||
Visibility="{Binding CanExportRawData, Source={x:Static settings:UserSettings.Default}, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<MenuItem.Header>
|
<MenuItem.Header>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding PlacementTarget.SelectedItem.Asset.Extension,
|
Text="{Binding PlacementTarget.SelectedItem.Asset.Extension,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||||
xmlns:settings="clr-namespace:FModel.Settings"
|
|
||||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||||
x:Class="FModel.Views.Resources.Controls.ContextMenus.FolderContextMenuDictionary">
|
x:Class="FModel.Views.Resources.Controls.ContextMenus.FolderContextMenuDictionary">
|
||||||
<ContextMenu x:Key="FolderContextMenu" x:Shared="False"
|
<ContextMenu x:Key="FolderContextMenu" x:Shared="False"
|
||||||
Opened="FolderContextMenu_OnOpened">
|
Opened="FolderContextMenu_OnOpened">
|
||||||
<MenuItem Header="Export Folder's Packages Raw Data (.uasset)"
|
<MenuItem Header="Export Folder's Packages Raw Data (.uasset)"
|
||||||
Command="{Binding RightClickMenuCommand}"
|
Command="{Binding RightClickMenuCommand}">
|
||||||
Visibility="{Binding CanExportRawData, Source={x:Static settings:UserSettings.Default}, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<MenuItem.CommandParameter>
|
<MenuItem.CommandParameter>
|
||||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||||
<Binding Source="Folders_Export_Data" />
|
<Binding Source="Folders_Export_Data" />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
@ -126,13 +126,40 @@ public class FLogger : ITextFormatter
|
||||||
{
|
{
|
||||||
NavigateUri = new Uri(url),
|
NavigateUri = new Uri(url),
|
||||||
OverridesDefaultStyle = true,
|
OverridesDefaultStyle = true,
|
||||||
Style = new Style(typeof(Hyperlink)) { Setters =
|
Style = new Style(typeof(Hyperlink))
|
||||||
{
|
{
|
||||||
new Setter(FrameworkContentElement.CursorProperty, Cursors.Hand),
|
Setters =
|
||||||
new Setter(TextBlock.TextDecorationsProperty, TextDecorations.Underline),
|
{
|
||||||
new Setter(TextElement.ForegroundProperty, Brushes.Cornsilk)
|
new Setter(FrameworkContentElement.CursorProperty, Cursors.Hand),
|
||||||
}}
|
new Setter(TextElement.ForegroundProperty, Brushes.Goldenrod),
|
||||||
}.Click += (sender, _) => Process.Start("explorer.exe", $"/select, \"{((Hyperlink)sender).NavigateUri.AbsoluteUri}\"");
|
new Setter(TextElement.FontWeightProperty, FontWeights.Bold)
|
||||||
|
},
|
||||||
|
Triggers =
|
||||||
|
{
|
||||||
|
new Trigger
|
||||||
|
{
|
||||||
|
Property = UIElement.IsMouseOverProperty,
|
||||||
|
Value = true,
|
||||||
|
Setters =
|
||||||
|
{
|
||||||
|
new Setter(TextElement.ForegroundProperty, Brushes.Gold),
|
||||||
|
new Setter(TextBlock.TextDecorationsProperty, TextDecorations.Underline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.Click += (sender, _) =>
|
||||||
|
{
|
||||||
|
var uri = ((Hyperlink) sender).NavigateUri;
|
||||||
|
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||||
|
{
|
||||||
|
Process.Start(new ProcessStartInfo(uri.AbsoluteUri) { UseShellExecute = true });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Process.Start("explorer.exe", $"/select, \"{uri.AbsoluteUri}\"");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -926,8 +926,7 @@
|
||||||
</MenuItem.IsEnabled>
|
</MenuItem.IsEnabled>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Command="{Binding TabCommand}" CommandParameter="Asset_Export_Data"
|
<MenuItem Command="{Binding TabCommand}" CommandParameter="Asset_Export_Data">
|
||||||
Visibility="{Binding CanExportRawData, Source={x:Static settings:UserSettings.Default}, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<MenuItem.Header>
|
<MenuItem.Header>
|
||||||
<TextBlock Text="{Binding Entry.Extension, FallbackValue='Export Raw Data', StringFormat='Export Raw Data (.{0})'}" />
|
<TextBlock Text="{Binding Entry.Extension, FallbackValue='Export Raw Data', StringFormat='Export Raw Data (.{0})'}" />
|
||||||
</MenuItem.Header>
|
</MenuItem.Header>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<adonisControls:AdonisWindow x:Class="FModel.Views.SearchView"
|
<adonisControls:AdonisWindow x:Class="FModel.Views.SearchView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:settings="clr-namespace:FModel.Settings"
|
|
||||||
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
|
||||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||||
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
|
||||||
|
|
@ -210,8 +209,7 @@
|
||||||
</MenuItem.IsEnabled>
|
</MenuItem.IsEnabled>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Command="{Binding DataContext.mainApplication.RightClickMenuCommand}"
|
<MenuItem Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
|
||||||
Visibility="{Binding CanExportRawData, Source={x:Static settings:UserSettings.Default}, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<MenuItem.Header>
|
<MenuItem.Header>
|
||||||
<TextBlock Text="{Binding DataContext.SelectedItem.Extension, FallbackValue='Export Raw Data',
|
<TextBlock Text="{Binding DataContext.SelectedItem.Extension, FallbackValue='Export Raw Data',
|
||||||
StringFormat='Export Raw Data (.{0})', RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
StringFormat='Export Raw Data (.{0})', RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||||
|
|
@ -556,8 +554,7 @@
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Command="{Binding DataContext.mainApplication.RightClickMenuCommand}"
|
<MenuItem Command="{Binding DataContext.mainApplication.RightClickMenuCommand}">
|
||||||
Visibility="{Binding CanExportRawData, Source={x:Static settings:UserSettings.Default}, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<MenuItem.Header>
|
<MenuItem.Header>
|
||||||
<TextBlock Text="{Binding DataContext.SelectedItem.Extension, FallbackValue='Export Raw Data',
|
<TextBlock Text="{Binding DataContext.SelectedItem.Extension, FallbackValue='Export Raw Data',
|
||||||
StringFormat='Export Raw Data (.{0})', RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
StringFormat='Export Raw Data (.{0})', RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
|
@ -223,51 +222,46 @@
|
||||||
<Button Grid.Column="2" Content="Mapping" Click="OpenMappingEndpoint" />
|
<Button Grid.Column="2" Content="Mapping" Click="OpenMappingEndpoint" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock Grid.Row="16" Grid.Column="0" Text="Allow Raw Data Export (.uasset)" VerticalAlignment="Center" Margin="0 0 0 5" />
|
<TextBlock Grid.Row="16" Grid.Column="0" Text="Serialize Script Bytecode" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||||
<CheckBox Grid.Row="16" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
<CheckBox Grid.Row="16" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||||
IsChecked="{Binding CanExportRawData, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
|
||||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
|
||||||
|
|
||||||
<TextBlock Grid.Row="17" Grid.Column="0" Text="Serialize Script Bytecode" VerticalAlignment="Center" Margin="0 0 0 5" />
|
|
||||||
<CheckBox Grid.Row="17" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
|
||||||
IsChecked="{Binding ReadScriptData, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
IsChecked="{Binding ReadScriptData, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
||||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="18" Grid.Column="0" Text="Serialize Inlined Shader Maps" VerticalAlignment="Center" Margin="0 0 0 5" />
|
<TextBlock Grid.Row="17" Grid.Column="0" Text="Serialize Inlined Shader Maps" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||||
<CheckBox Grid.Row="18" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
<CheckBox Grid.Row="17" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||||
IsChecked="{Binding ReadShaderMaps, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
IsChecked="{Binding ReadShaderMaps, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
||||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="19" Grid.Column="0" Text="Decompile Blueprint to Pseudo C++" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Adds a right click option to decompile UClass packages into a pseudo C++ friendly format" />
|
<TextBlock Grid.Row="18" Grid.Column="0" Text="Decompile Blueprint to Pseudo C++" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Adds a right click option to decompile UClass packages into a pseudo C++ friendly format" />
|
||||||
<CheckBox Grid.Row="19" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
<CheckBox Grid.Row="18" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||||
IsChecked="{Binding ShowDecompileOption, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
IsChecked="{Binding ShowDecompileOption, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
||||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="20"
|
<TextBlock Grid.Row="19"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Text="Convert Audio During Export (.wav)"
|
Text="Convert Audio During Export (.wav)"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0 0 0 5" />
|
Margin="0 0 0 5" />
|
||||||
<CheckBox Grid.Row="20"
|
<CheckBox Grid.Row="19"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||||
IsChecked="{Binding ConvertAudioOnBulkExport, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
IsChecked="{Binding ConvertAudioOnBulkExport, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||||
Margin="0 5 0 10"
|
Margin="0 5 0 10"
|
||||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="21" Grid.Column="0" Text="Max Wwise Bank (.BNK) Prefetch" VerticalAlignment="Center" Margin="0 0 0 5" />
|
<TextBlock Grid.Row="20" Grid.Column="0" Text="Max Wwise Bank (.BNK) Prefetch" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||||
<Slider Grid.Row="21" Grid.Column="2" Grid.ColumnSpan="5" TickPlacement="None" Minimum="0" Maximum="2048" Ticks="0,8,32,128,256,512,1024,2048"
|
<Slider Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="5" TickPlacement="None" Minimum="0" Maximum="2048" Ticks="0,8,32,128,256,512,1024,2048"
|
||||||
AutoToolTipPlacement="BottomRight" IsMoveToPointEnabled="True" IsSnapToTickEnabled="True" Margin="0 5 0 5"
|
AutoToolTipPlacement="BottomRight" IsMoveToPointEnabled="True" IsSnapToTickEnabled="True" Margin="0 5 0 5"
|
||||||
Value="{Binding WwiseMaxBnkPrefetch, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"/>
|
Value="{Binding WwiseMaxBnkPrefetch, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="22"
|
<TextBlock Grid.Row="21"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Text="CRIWARE Decryption Key"
|
Text="CRIWARE Decryption Key"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0 0 0 10" />
|
Margin="0 0 0 10" />
|
||||||
|
|
||||||
<TextBox x:Name="CriwareKeyBox"
|
<TextBox x:Name="CriwareKeyBox"
|
||||||
Grid.Row="22"
|
Grid.Row="21"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Grid.ColumnSpan="5"
|
Grid.ColumnSpan="5"
|
||||||
Margin="0 5 0 10"
|
Margin="0 5 0 10"
|
||||||
|
|
@ -398,6 +392,8 @@
|
||||||
<ContentControl.Style>
|
<ContentControl.Style>
|
||||||
<Style TargetType="{x:Type ContentControl}">
|
<Style TargetType="{x:Type ContentControl}">
|
||||||
<Setter Property="Visibility" Value="Collapsed" />
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
|
<EventSetter Event="Hyperlink.Click" Handler="OnHyperlinkClick" />
|
||||||
|
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
<DataTrigger Binding="{Binding DataContext.SettingsView.SelectedMeshExportFormat, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Value="{x:Static c4pMeshes:EMeshFormat.UEFormat}">
|
<DataTrigger Binding="{Binding DataContext.SettingsView.SelectedMeshExportFormat, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}" Value="{x:Static c4pMeshes:EMeshFormat.UEFormat}">
|
||||||
<Setter Property="ContentTemplate">
|
<Setter Property="ContentTemplate">
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Documents;
|
||||||
using FModel.Services;
|
using FModel.Services;
|
||||||
using FModel.Settings;
|
using FModel.Settings;
|
||||||
using FModel.ViewModels;
|
using FModel.ViewModels;
|
||||||
|
|
@ -241,4 +243,12 @@ public partial class SettingsView
|
||||||
out value
|
out value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnHyperlinkClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.OriginalSource is not Hyperlink hyperlink)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Process.Start(new ProcessStartInfo(hyperlink.NavigateUri.AbsoluteUri) { UseShellExecute = true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ public abstract class UModel : IRenderableModel
|
||||||
{
|
{
|
||||||
_export = export;
|
_export = export;
|
||||||
Path = _export.GetPathName();
|
Path = _export.GetPathName();
|
||||||
Name = Path.SubstringAfterLast('/').SubstringBefore('.');
|
Name = export.Name;
|
||||||
Type = export.ExportType;
|
Type = export.ExportType;
|
||||||
UvCount = 1;
|
UvCount = 1;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user