mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
This commit is contained in:
commit
d58acea554
|
|
@ -1 +1 @@
|
|||
Subproject commit e63d6ee96fba5e5742dcd9bdf7f834e95c3e4f8f
|
||||
Subproject commit 0f442379903ce789dfef2ed2c784120525036b81
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
|
|
@ -24,7 +24,8 @@ public class DirectorySettings : ViewModel, ICloneable
|
|||
Endpoints = old?.Endpoints ?? EndpointSettings.Default(gameName),
|
||||
Directories = old?.Directories ?? CustomDirectory.Default(gameName),
|
||||
AesKeys = old?.AesKeys ?? new AesResponse { MainKey = aes, DynamicKeys = null },
|
||||
LastAesReload = old?.LastAesReload ?? DateTime.Today.AddDays(-1)
|
||||
LastAesReload = old?.LastAesReload ?? DateTime.Today.AddDays(-1),
|
||||
CriwareDecryptionKey = old?.CriwareDecryptionKey ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +99,13 @@ public class DirectorySettings : ViewModel, ICloneable
|
|||
set => SetProperty(ref _lastAesReload, value);
|
||||
}
|
||||
|
||||
private ulong _criwareDecryptionKey;
|
||||
public ulong CriwareDecryptionKey
|
||||
{
|
||||
get => _criwareDecryptionKey;
|
||||
set => SetProperty(ref _criwareDecryptionKey, value);
|
||||
}
|
||||
|
||||
private bool Equals(DirectorySettings other)
|
||||
{
|
||||
return GameDirectory == other.GameDirectory && UeVersion == other.UeVersion;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
using CSCore;
|
||||
using CSCore.DSP;
|
||||
using CSCore.SoundOut;
|
||||
using CSCore.Streams;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
|
@ -12,7 +8,14 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using CSCore;
|
||||
using CSCore.CoreAudioAPI;
|
||||
using CSCore.DSP;
|
||||
using CSCore.SoundOut;
|
||||
using CSCore.Streams;
|
||||
using CUE4Parse.UE4.CriWare.Decoders;
|
||||
using CUE4Parse.UE4.CriWare.Decoders.ADX;
|
||||
using CUE4Parse.UE4.CriWare.Decoders.HCA;
|
||||
using CUE4Parse.Utils;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
|
|
@ -579,6 +582,9 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
|||
|
||||
return false;
|
||||
}
|
||||
case "adx":
|
||||
case "hca":
|
||||
return TryConvertCriware();
|
||||
case "rada":
|
||||
case "binka":
|
||||
{
|
||||
|
|
@ -596,6 +602,48 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool TryConvertCriware()
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] wavData = SelectedAudioFile.Extension switch
|
||||
{
|
||||
"hca" => HcaWaveStream.ConvertHcaToWav(
|
||||
SelectedAudioFile.Data,
|
||||
UserSettings.Default.CurrentDir.CriwareDecryptionKey),
|
||||
"adx" => AdxDecoder.ConvertAdxToWav(
|
||||
SelectedAudioFile.Data,
|
||||
UserSettings.Default.CurrentDir.CriwareDecryptionKey),
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
string wavFilePath = Path.Combine(
|
||||
UserSettings.Default.AudioDirectory,
|
||||
SelectedAudioFile.FilePath.TrimStart('/'));
|
||||
wavFilePath = Path.ChangeExtension(wavFilePath, ".wav");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(wavFilePath)!);
|
||||
File.WriteAllBytes(wavFilePath, wavData);
|
||||
|
||||
var newAudio = new AudioFile(SelectedAudioFile.Id, new FileInfo(wavFilePath));
|
||||
Replace(newAudio);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (CriwareDecryptionException ex)
|
||||
{
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text($"Encrypted {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}", Constants.WHITE, true));
|
||||
Log.Error($"Encrypted {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text($"Failed to convert {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}", Constants.WHITE, true));
|
||||
Log.Error($"Failed to convert {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryConvert(out string wavFilePath) => TryConvert(SelectedAudioFile.FilePath, SelectedAudioFile.Data, out wavFilePath);
|
||||
private bool TryConvert(string inputFilePath, byte[] inputFileData, out string wavFilePath)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ using CUE4Parse.UE4.AssetRegistry;
|
|||
using CUE4Parse.UE4.Assets;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Animation;
|
||||
using CUE4Parse.UE4.Assets.Exports.CriWare;
|
||||
using CUE4Parse.UE4.Assets.Exports.Fmod;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
|
||||
|
|
@ -33,6 +34,8 @@ using CUE4Parse.UE4.Assets.Exports.Texture;
|
|||
using CUE4Parse.UE4.Assets.Exports.Verse;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.BinaryConfig;
|
||||
using CUE4Parse.UE4.CriWare;
|
||||
using CUE4Parse.UE4.CriWare.Readers;
|
||||
using CUE4Parse.UE4.FMod;
|
||||
using CUE4Parse.UE4.IO;
|
||||
using CUE4Parse.UE4.Localization;
|
||||
|
|
@ -135,6 +138,8 @@ public class CUE4ParseViewModel : ViewModel
|
|||
public WwiseProvider WwiseProvider => _wwiseProviderLazy.Value;
|
||||
private Lazy<FModProvider> _fmodProviderLazy;
|
||||
public FModProvider FmodProvider => _fmodProviderLazy?.Value;
|
||||
private Lazy<CriWareProvider> _criWareProviderLazy;
|
||||
public CriWareProvider CriWareProvider => _criWareProviderLazy?.Value;
|
||||
public ConcurrentBag<string> UnknownExtensions = [];
|
||||
|
||||
public CUE4ParseViewModel()
|
||||
|
|
@ -289,6 +294,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
Provider.Initialize();
|
||||
_wwiseProviderLazy = new Lazy<WwiseProvider>(() => new WwiseProvider(Provider, UserSettings.Default.WwiseMaxBnkPrefetch));
|
||||
_fmodProviderLazy = new Lazy<FModProvider>(() => new FModProvider(Provider, UserSettings.Default.GameDirectory));
|
||||
_criWareProviderLazy = new Lazy<CriWareProvider>(() => new CriWareProvider(Provider, UserSettings.Default.GameDirectory));
|
||||
Log.Information($"{Provider.Versions.Game} ({Provider.Versions.Platform}) | Archives: x{Provider.UnloadedVfs.Count} | AES: x{Provider.RequiredKeys.Count} | Loose Files: x{Provider.Files.Count}");
|
||||
});
|
||||
}
|
||||
|
|
@ -642,6 +648,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
case "dnearchive": // Banishers: Ghosts of New Eden
|
||||
case "gitignore":
|
||||
case "LICENSE":
|
||||
case "playstats": // Dispatch
|
||||
case "template":
|
||||
case "stUMeta": // LIS: Double Exposure
|
||||
case "vmodule":
|
||||
|
|
@ -770,6 +777,38 @@ public class CUE4ParseViewModel : ViewModel
|
|||
|
||||
break;
|
||||
}
|
||||
case "awb":
|
||||
{
|
||||
var archive = entry.CreateReader();
|
||||
var awbReader = new AwbReader(archive);
|
||||
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(awbReader, Formatting.Indented), saveProperties, updateUi);
|
||||
|
||||
var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/";
|
||||
var extractedSounds = CriWareProvider.ExtractCriWareSounds(awbReader, archive.Name);
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "acb":
|
||||
{
|
||||
var archive = entry.CreateReader();
|
||||
var acbReader = new AcbReader(archive);
|
||||
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(acbReader, Formatting.Indented), saveProperties, updateUi);
|
||||
|
||||
var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/";
|
||||
var extractedSounds = CriWareProvider.ExtractCriWareSounds(acbReader, archive.Name);
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "xvag":
|
||||
case "flac":
|
||||
case "at9":
|
||||
|
|
@ -995,6 +1034,25 @@ public class CUE4ParseViewModel : ViewModel
|
|||
}
|
||||
return false;
|
||||
}
|
||||
case USoundAtomCueSheet or UAtomCueSheet or USoundAtomCue or UAtomWaveBank when (isNone || saveAudio) && pointer.Object.Value is UObject atomObject:
|
||||
{
|
||||
var extractedSounds = atomObject switch
|
||||
{
|
||||
USoundAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet),
|
||||
UAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet),
|
||||
USoundAtomCue cue => CriWareProvider.ExtractCriWareSounds(cue),
|
||||
UAtomWaveBank awb => CriWareProvider.ExtractCriWareSounds(awb),
|
||||
_ => []
|
||||
};
|
||||
|
||||
var directory = Path.GetDirectoryName(atomObject.Owner?.Name) ?? "/Criware/";
|
||||
directory = Path.GetDirectoryName(atomObject.Owner.Provider.FixPath(directory));
|
||||
foreach (var sound in extractedSounds)
|
||||
{
|
||||
SaveAndPlaySound(Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case UAkMediaAssetData when isNone || saveAudio:
|
||||
case USoundWave when isNone || saveAudio:
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Nanite;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.Core.Serialization;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using CUE4Parse_Conversion.UEFormat.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Nanite;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
|
|
@ -165,6 +165,13 @@ public class SettingsViewModel : ViewModel
|
|||
set => SetProperty(ref _selectedTextureExportFormat, value);
|
||||
}
|
||||
|
||||
private ulong _criwareDecryptionKey;
|
||||
public ulong CriwareDecryptionKey
|
||||
{
|
||||
get => _criwareDecryptionKey;
|
||||
set => SetProperty(ref _criwareDecryptionKey, value);
|
||||
}
|
||||
|
||||
public bool SocketSettingsEnabled => SelectedMeshExportFormat == EMeshFormat.ActorX;
|
||||
public bool CompressionSettingsEnabled => SelectedMeshExportFormat == EMeshFormat.UEFormat;
|
||||
|
||||
|
|
@ -227,6 +234,7 @@ public class SettingsViewModel : ViewModel
|
|||
_customVersionsSnapshot = UserSettings.Default.CurrentDir.Versioning.CustomVersions;
|
||||
_optionsSnapshot = UserSettings.Default.CurrentDir.Versioning.Options;
|
||||
_mapStructTypesSnapshot = UserSettings.Default.CurrentDir.Versioning.MapStructTypes;
|
||||
_criwareDecryptionKey = UserSettings.Default.CurrentDir.CriwareDecryptionKey;
|
||||
|
||||
AesEndpoint = UserSettings.Default.CurrentDir.Endpoints[0];
|
||||
MappingEndpoint = UserSettings.Default.CurrentDir.Endpoints[1];
|
||||
|
|
@ -262,6 +270,7 @@ public class SettingsViewModel : ViewModel
|
|||
SelectedNaniteMeshExportFormat = _naniteMeshExportFormatSnapshot;
|
||||
SelectedMaterialExportFormat = _materialExportFormatSnapshot;
|
||||
SelectedTextureExportFormat = _textureExportFormatSnapshot;
|
||||
CriwareDecryptionKey = _criwareDecryptionKey;
|
||||
SelectedAesReload = UserSettings.Default.AesReload;
|
||||
SelectedDiscordRpc = UserSettings.Default.DiscordRpc;
|
||||
|
||||
|
|
@ -308,6 +317,7 @@ public class SettingsViewModel : ViewModel
|
|||
UserSettings.Default.CurrentDir.Versioning.CustomVersions = SelectedCustomVersions;
|
||||
UserSettings.Default.CurrentDir.Versioning.Options = SelectedOptions;
|
||||
UserSettings.Default.CurrentDir.Versioning.MapStructTypes = SelectedMapStructTypes;
|
||||
UserSettings.Default.CurrentDir.CriwareDecryptionKey = CriwareDecryptionKey;
|
||||
|
||||
UserSettings.Default.AssetLanguage = SelectedAssetLanguage;
|
||||
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
|
@ -238,6 +239,26 @@
|
|||
<Slider Grid.Row="19" 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"
|
||||
Value="{Binding WwiseMaxBnkPrefetch, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"/>
|
||||
|
||||
<TextBlock Grid.Row="20"
|
||||
Grid.Column="0"
|
||||
Text="CRIWARE Decryption Key"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 0 10" />
|
||||
|
||||
<TextBox x:Name="CriwareKeyBox"
|
||||
Grid.Row="20"
|
||||
Grid.Column="2"
|
||||
Grid.ColumnSpan="5"
|
||||
Margin="0 5 0 10"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
MaxLength="20"
|
||||
ToolTip="Enter decryption key in numeric or hexadecimal format (valid key is up to 20 digits or 8 bytes long)"
|
||||
TextAlignment="Right"
|
||||
TextChanged="CriwareKeyBox_TextChanged"
|
||||
Loaded="CriwareKeyBox_Loaded"/>
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="CreatorTemplate">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using FModel.Services;
|
||||
|
|
@ -190,4 +193,52 @@ public partial class SettingsView
|
|||
_applicationView.SettingsView.MappingEndpoint, "Endpoint Configuration (Mapping)", EEndpointType.Mapping);
|
||||
editor.ShowDialog();
|
||||
}
|
||||
|
||||
private void CriwareKeyBox_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not TextBox textBox)
|
||||
return;
|
||||
|
||||
textBox.Text = _applicationView.SettingsView.CriwareDecryptionKey.ToString();
|
||||
}
|
||||
|
||||
private void CriwareKeyBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (sender is not TextBox textBox)
|
||||
return;
|
||||
|
||||
string input = textBox.Text?.Trim() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return;
|
||||
|
||||
if (TryParseKey(input, out ulong parsed))
|
||||
_applicationView.SettingsView.CriwareDecryptionKey = parsed;
|
||||
}
|
||||
|
||||
private static bool TryParseKey(string text, out ulong value)
|
||||
{
|
||||
value = 0;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return false;
|
||||
|
||||
bool isHex = false;
|
||||
if (text.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
isHex = true;
|
||||
text = text[2..];
|
||||
}
|
||||
else if (text.Any(char.IsLetter))
|
||||
{
|
||||
isHex = true;
|
||||
}
|
||||
|
||||
int numberBase = text.All(Uri.IsHexDigit) ? 16 : 10;
|
||||
return ulong.TryParse(
|
||||
text,
|
||||
isHex ? NumberStyles.HexNumber : NumberStyles.Integer,
|
||||
CultureInfo.InvariantCulture,
|
||||
out value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,6 +240,18 @@ public class Options
|
|||
Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip emmisive for specific games, cause of excessive use in their materials
|
||||
/// </summary>
|
||||
public bool SkipEmmisive()
|
||||
{
|
||||
return _game switch
|
||||
{
|
||||
"LIESOFP" => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public void ResetModelsLightsAnimations()
|
||||
{
|
||||
foreach (var model in Models.Values)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
|
@ -121,10 +121,15 @@ public class Material : IDisposable
|
|||
RoughnessMax = roughness + d;
|
||||
}
|
||||
|
||||
if (Parameters.TryGetScalar(out var emissiveMultScalar, "emissive mult", "Emissive_Mult", "EmissiveIntensity", "EmissionIntensity"))
|
||||
EmissiveMult = emissiveMultScalar;
|
||||
else if (Parameters.TryGetLinearColor(out var emissiveMultColor, "Emissive Multiplier", "EmissiveMultiplier"))
|
||||
EmissiveMult = emissiveMultColor.R;
|
||||
if (!options.SkipEmmisive())
|
||||
{
|
||||
if (Parameters.TryGetScalar(out var emissiveMultScalar, "emissive mult", "Emissive_Mult", "EmissiveIntensity", "EmissionIntensity"))
|
||||
EmissiveMult = emissiveMultScalar;
|
||||
else if (Parameters.TryGetLinearColor(out var emissiveMultColor, "Emissive Multiplier", "EmissiveMultiplier"))
|
||||
EmissiveMult = emissiveMultColor.R;
|
||||
}
|
||||
else
|
||||
EmissiveMult = 0f;
|
||||
|
||||
if (Parameters.TryGetLinearColor(out var EmissiveUVs,
|
||||
"EmissiveUVs_RG_UpperLeftCorner_BA_LowerRightCorner",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user