CRIWARE Support
Some checks failed
FModel QA Builder / build (push) Has been cancelled

This commit is contained in:
LongerWarrior 2025-11-12 13:01:00 +02:00 committed by GitHub
commit d58acea554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 227 additions and 14 deletions

@ -1 +1 @@
Subproject commit e63d6ee96fba5e5742dcd9bdf7f834e95c3e4f8f
Subproject commit 0f442379903ce789dfef2ed2c784120525036b81

View File

@ -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;

View File

@ -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)
{

View File

@ -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:
{

View File

@ -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;

View File

@ -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">

View File

@ -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
);
}
}

View File

@ -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)

View File

@ -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",