Skip to content

Commit d58acea

Browse files
CRIWARE Support
2 parents 604e59b + 5b13bb0 commit d58acea

File tree

9 files changed

+227
-14
lines changed

9 files changed

+227
-14
lines changed

CUE4Parse

Submodule CUE4Parse updated 68 files

FModel/Settings/DirectorySettings.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using CUE4Parse.UE4.Assets.Exports.Texture;
44
using CUE4Parse.UE4.Versions;
@@ -24,7 +24,8 @@ public static DirectorySettings Default(
2424
Endpoints = old?.Endpoints ?? EndpointSettings.Default(gameName),
2525
Directories = old?.Directories ?? CustomDirectory.Default(gameName),
2626
AesKeys = old?.AesKeys ?? new AesResponse { MainKey = aes, DynamicKeys = null },
27-
LastAesReload = old?.LastAesReload ?? DateTime.Today.AddDays(-1)
27+
LastAesReload = old?.LastAesReload ?? DateTime.Today.AddDays(-1),
28+
CriwareDecryptionKey = old?.CriwareDecryptionKey ?? 0
2829
};
2930
}
3031

@@ -98,6 +99,13 @@ public DateTime LastAesReload
9899
set => SetProperty(ref _lastAesReload, value);
99100
}
100101

102+
private ulong _criwareDecryptionKey;
103+
public ulong CriwareDecryptionKey
104+
{
105+
get => _criwareDecryptionKey;
106+
set => SetProperty(ref _criwareDecryptionKey, value);
107+
}
108+
101109
private bool Equals(DirectorySettings other)
102110
{
103111
return GameDirectory == other.GameDirectory && UeVersion == other.UeVersion;

FModel/ViewModels/AudioPlayerViewModel.cs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
using CSCore;
2-
using CSCore.DSP;
3-
using CSCore.SoundOut;
4-
using CSCore.Streams;
51
using System;
62
using System.Collections.Generic;
73
using System.Collections.ObjectModel;
@@ -12,7 +8,14 @@
128
using System.Threading;
139
using System.Windows;
1410
using System.Windows.Data;
11+
using CSCore;
1512
using CSCore.CoreAudioAPI;
13+
using CSCore.DSP;
14+
using CSCore.SoundOut;
15+
using CSCore.Streams;
16+
using CUE4Parse.UE4.CriWare.Decoders;
17+
using CUE4Parse.UE4.CriWare.Decoders.ADX;
18+
using CUE4Parse.UE4.CriWare.Decoders.HCA;
1619
using CUE4Parse.Utils;
1720
using FModel.Extensions;
1821
using FModel.Framework;
@@ -579,6 +582,9 @@ private bool ConvertIfNeeded()
579582

580583
return false;
581584
}
585+
case "adx":
586+
case "hca":
587+
return TryConvertCriware();
582588
case "rada":
583589
case "binka":
584590
{
@@ -596,6 +602,48 @@ private bool ConvertIfNeeded()
596602
return true;
597603
}
598604

605+
private bool TryConvertCriware()
606+
{
607+
try
608+
{
609+
byte[] wavData = SelectedAudioFile.Extension switch
610+
{
611+
"hca" => HcaWaveStream.ConvertHcaToWav(
612+
SelectedAudioFile.Data,
613+
UserSettings.Default.CurrentDir.CriwareDecryptionKey),
614+
"adx" => AdxDecoder.ConvertAdxToWav(
615+
SelectedAudioFile.Data,
616+
UserSettings.Default.CurrentDir.CriwareDecryptionKey),
617+
_ => throw new NotSupportedException()
618+
};
619+
620+
string wavFilePath = Path.Combine(
621+
UserSettings.Default.AudioDirectory,
622+
SelectedAudioFile.FilePath.TrimStart('/'));
623+
wavFilePath = Path.ChangeExtension(wavFilePath, ".wav");
624+
625+
Directory.CreateDirectory(Path.GetDirectoryName(wavFilePath)!);
626+
File.WriteAllBytes(wavFilePath, wavData);
627+
628+
var newAudio = new AudioFile(SelectedAudioFile.Id, new FileInfo(wavFilePath));
629+
Replace(newAudio);
630+
631+
return true;
632+
}
633+
catch (CriwareDecryptionException ex)
634+
{
635+
FLogger.Append(ELog.Error, () => FLogger.Text($"Encrypted {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}", Constants.WHITE, true));
636+
Log.Error($"Encrypted {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}");
637+
return false;
638+
}
639+
catch (Exception ex)
640+
{
641+
FLogger.Append(ELog.Error, () => FLogger.Text($"Failed to convert {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}", Constants.WHITE, true));
642+
Log.Error($"Failed to convert {SelectedAudioFile.Extension.ToUpper()}: {ex.Message}");
643+
return false;
644+
}
645+
}
646+
599647
private bool TryConvert(out string wavFilePath) => TryConvert(SelectedAudioFile.FilePath, SelectedAudioFile.Data, out wavFilePath);
600648
private bool TryConvert(string inputFilePath, byte[] inputFileData, out string wavFilePath)
601649
{

FModel/ViewModels/CUE4ParseViewModel.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using CUE4Parse.UE4.Assets;
2525
using CUE4Parse.UE4.Assets.Exports;
2626
using CUE4Parse.UE4.Assets.Exports.Animation;
27+
using CUE4Parse.UE4.Assets.Exports.CriWare;
2728
using CUE4Parse.UE4.Assets.Exports.Fmod;
2829
using CUE4Parse.UE4.Assets.Exports.Material;
2930
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
@@ -33,6 +34,8 @@
3334
using CUE4Parse.UE4.Assets.Exports.Verse;
3435
using CUE4Parse.UE4.Assets.Exports.Wwise;
3536
using CUE4Parse.UE4.BinaryConfig;
37+
using CUE4Parse.UE4.CriWare;
38+
using CUE4Parse.UE4.CriWare.Readers;
3639
using CUE4Parse.UE4.FMod;
3740
using CUE4Parse.UE4.IO;
3841
using CUE4Parse.UE4.Localization;
@@ -135,6 +138,8 @@ public Snooper SnooperViewer
135138
public WwiseProvider WwiseProvider => _wwiseProviderLazy.Value;
136139
private Lazy<FModProvider> _fmodProviderLazy;
137140
public FModProvider FmodProvider => _fmodProviderLazy?.Value;
141+
private Lazy<CriWareProvider> _criWareProviderLazy;
142+
public CriWareProvider CriWareProvider => _criWareProviderLazy?.Value;
138143
public ConcurrentBag<string> UnknownExtensions = [];
139144

140145
public CUE4ParseViewModel()
@@ -289,6 +294,7 @@ await _threadWorkerView.Begin(cancellationToken =>
289294
Provider.Initialize();
290295
_wwiseProviderLazy = new Lazy<WwiseProvider>(() => new WwiseProvider(Provider, UserSettings.Default.WwiseMaxBnkPrefetch));
291296
_fmodProviderLazy = new Lazy<FModProvider>(() => new FModProvider(Provider, UserSettings.Default.GameDirectory));
297+
_criWareProviderLazy = new Lazy<CriWareProvider>(() => new CriWareProvider(Provider, UserSettings.Default.GameDirectory));
292298
Log.Information($"{Provider.Versions.Game} ({Provider.Versions.Platform}) | Archives: x{Provider.UnloadedVfs.Count} | AES: x{Provider.RequiredKeys.Count} | Loose Files: x{Provider.Files.Count}");
293299
});
294300
}
@@ -642,6 +648,7 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
642648
case "dnearchive": // Banishers: Ghosts of New Eden
643649
case "gitignore":
644650
case "LICENSE":
651+
case "playstats": // Dispatch
645652
case "template":
646653
case "stUMeta": // LIS: Double Exposure
647654
case "vmodule":
@@ -770,6 +777,38 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
770777

771778
break;
772779
}
780+
case "awb":
781+
{
782+
var archive = entry.CreateReader();
783+
var awbReader = new AwbReader(archive);
784+
785+
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(awbReader, Formatting.Indented), saveProperties, updateUi);
786+
787+
var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/";
788+
var extractedSounds = CriWareProvider.ExtractCriWareSounds(awbReader, archive.Name);
789+
foreach (var sound in extractedSounds)
790+
{
791+
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
792+
}
793+
794+
break;
795+
}
796+
case "acb":
797+
{
798+
var archive = entry.CreateReader();
799+
var acbReader = new AcbReader(archive);
800+
801+
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(acbReader, Formatting.Indented), saveProperties, updateUi);
802+
803+
var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/";
804+
var extractedSounds = CriWareProvider.ExtractCriWareSounds(acbReader, archive.Name);
805+
foreach (var sound in extractedSounds)
806+
{
807+
SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio);
808+
}
809+
810+
break;
811+
}
773812
case "xvag":
774813
case "flac":
775814
case "at9":
@@ -995,6 +1034,25 @@ public void ExtractAndScroll(CancellationToken cancellationToken, string fullPat
9951034
}
9961035
return false;
9971036
}
1037+
case USoundAtomCueSheet or UAtomCueSheet or USoundAtomCue or UAtomWaveBank when (isNone || saveAudio) && pointer.Object.Value is UObject atomObject:
1038+
{
1039+
var extractedSounds = atomObject switch
1040+
{
1041+
USoundAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet),
1042+
UAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet),
1043+
USoundAtomCue cue => CriWareProvider.ExtractCriWareSounds(cue),
1044+
UAtomWaveBank awb => CriWareProvider.ExtractCriWareSounds(awb),
1045+
_ => []
1046+
};
1047+
1048+
var directory = Path.GetDirectoryName(atomObject.Owner?.Name) ?? "/Criware/";
1049+
directory = Path.GetDirectoryName(atomObject.Owner.Provider.FixPath(directory));
1050+
foreach (var sound in extractedSounds)
1051+
{
1052+
SaveAndPlaySound(Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio);
1053+
}
1054+
return false;
1055+
}
9981056
case UAkMediaAssetData when isNone || saveAudio:
9991057
case USoundWave when isNone || saveAudio:
10001058
{

FModel/ViewModels/SettingsViewModel.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
using System.Collections.Generic;
33
using System.Collections.ObjectModel;
44
using System.Linq;
5+
using CUE4Parse.UE4.Assets.Exports.Material;
6+
using CUE4Parse.UE4.Assets.Exports.Nanite;
57
using CUE4Parse.UE4.Assets.Exports.Texture;
68
using CUE4Parse.UE4.Objects.Core.Serialization;
79
using CUE4Parse.UE4.Versions;
810
using CUE4Parse_Conversion.Meshes;
911
using CUE4Parse_Conversion.Textures;
1012
using CUE4Parse_Conversion.UEFormat.Enums;
11-
using CUE4Parse.UE4.Assets.Exports.Material;
12-
using CUE4Parse.UE4.Assets.Exports.Nanite;
1313
using FModel.Framework;
1414
using FModel.Services;
1515
using FModel.Settings;
@@ -165,6 +165,13 @@ public ETextureFormat SelectedTextureExportFormat
165165
set => SetProperty(ref _selectedTextureExportFormat, value);
166166
}
167167

168+
private ulong _criwareDecryptionKey;
169+
public ulong CriwareDecryptionKey
170+
{
171+
get => _criwareDecryptionKey;
172+
set => SetProperty(ref _criwareDecryptionKey, value);
173+
}
174+
168175
public bool SocketSettingsEnabled => SelectedMeshExportFormat == EMeshFormat.ActorX;
169176
public bool CompressionSettingsEnabled => SelectedMeshExportFormat == EMeshFormat.UEFormat;
170177

@@ -227,6 +234,7 @@ public void Initialize()
227234
_customVersionsSnapshot = UserSettings.Default.CurrentDir.Versioning.CustomVersions;
228235
_optionsSnapshot = UserSettings.Default.CurrentDir.Versioning.Options;
229236
_mapStructTypesSnapshot = UserSettings.Default.CurrentDir.Versioning.MapStructTypes;
237+
_criwareDecryptionKey = UserSettings.Default.CurrentDir.CriwareDecryptionKey;
230238

231239
AesEndpoint = UserSettings.Default.CurrentDir.Endpoints[0];
232240
MappingEndpoint = UserSettings.Default.CurrentDir.Endpoints[1];
@@ -262,6 +270,7 @@ public void Initialize()
262270
SelectedNaniteMeshExportFormat = _naniteMeshExportFormatSnapshot;
263271
SelectedMaterialExportFormat = _materialExportFormatSnapshot;
264272
SelectedTextureExportFormat = _textureExportFormatSnapshot;
273+
CriwareDecryptionKey = _criwareDecryptionKey;
265274
SelectedAesReload = UserSettings.Default.AesReload;
266275
SelectedDiscordRpc = UserSettings.Default.DiscordRpc;
267276

@@ -308,6 +317,7 @@ public bool Save(out List<SettingsOut> whatShouldIDo)
308317
UserSettings.Default.CurrentDir.Versioning.CustomVersions = SelectedCustomVersions;
309318
UserSettings.Default.CurrentDir.Versioning.Options = SelectedOptions;
310319
UserSettings.Default.CurrentDir.Versioning.MapStructTypes = SelectedMapStructTypes;
320+
UserSettings.Default.CurrentDir.CriwareDecryptionKey = CriwareDecryptionKey;
311321

312322
UserSettings.Default.AssetLanguage = SelectedAssetLanguage;
313323
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;

FModel/Views/SettingsView.xaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<RowDefinition Height="Auto" />
4747
<RowDefinition Height="Auto" />
4848
<RowDefinition Height="Auto" />
49+
<RowDefinition Height="Auto" />
4950
</Grid.RowDefinitions>
5051
<Grid.ColumnDefinitions>
5152
<ColumnDefinition Width="Auto" />
@@ -238,6 +239,26 @@
238239
<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"
239240
AutoToolTipPlacement="BottomRight" IsMoveToPointEnabled="True" IsSnapToTickEnabled="True" Margin="0 5 0 5"
240241
Value="{Binding WwiseMaxBnkPrefetch, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"/>
242+
243+
<TextBlock Grid.Row="20"
244+
Grid.Column="0"
245+
Text="CRIWARE Decryption Key"
246+
VerticalAlignment="Center"
247+
Margin="0 0 0 10" />
248+
249+
<TextBox x:Name="CriwareKeyBox"
250+
Grid.Row="20"
251+
Grid.Column="2"
252+
Grid.ColumnSpan="5"
253+
Margin="0 5 0 10"
254+
VerticalAlignment="Center"
255+
VerticalContentAlignment="Center"
256+
MaxLength="20"
257+
ToolTip="Enter decryption key in numeric or hexadecimal format (valid key is up to 20 digits or 8 bytes long)"
258+
TextAlignment="Right"
259+
TextChanged="CriwareKeyBox_TextChanged"
260+
Loaded="CriwareKeyBox_Loaded"/>
261+
241262
</Grid>
242263
</DataTemplate>
243264
<DataTemplate x:Key="CreatorTemplate">

FModel/Views/SettingsView.xaml.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
using System;
2+
using System.Globalization;
13
using System.IO;
4+
using System.Linq;
25
using System.Windows;
36
using System.Windows.Controls;
47
using FModel.Services;
@@ -190,4 +193,52 @@ private void OpenMappingEndpoint(object sender, RoutedEventArgs e)
190193
_applicationView.SettingsView.MappingEndpoint, "Endpoint Configuration (Mapping)", EEndpointType.Mapping);
191194
editor.ShowDialog();
192195
}
196+
197+
private void CriwareKeyBox_Loaded(object sender, RoutedEventArgs e)
198+
{
199+
if (sender is not TextBox textBox)
200+
return;
201+
202+
textBox.Text = _applicationView.SettingsView.CriwareDecryptionKey.ToString();
203+
}
204+
205+
private void CriwareKeyBox_TextChanged(object sender, TextChangedEventArgs e)
206+
{
207+
if (sender is not TextBox textBox)
208+
return;
209+
210+
string input = textBox.Text?.Trim() ?? string.Empty;
211+
212+
if (string.IsNullOrEmpty(input))
213+
return;
214+
215+
if (TryParseKey(input, out ulong parsed))
216+
_applicationView.SettingsView.CriwareDecryptionKey = parsed;
217+
}
218+
219+
private static bool TryParseKey(string text, out ulong value)
220+
{
221+
value = 0;
222+
if (string.IsNullOrWhiteSpace(text))
223+
return false;
224+
225+
bool isHex = false;
226+
if (text.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
227+
{
228+
isHex = true;
229+
text = text[2..];
230+
}
231+
else if (text.Any(char.IsLetter))
232+
{
233+
isHex = true;
234+
}
235+
236+
int numberBase = text.All(Uri.IsHexDigit) ? 16 : 10;
237+
return ulong.TryParse(
238+
text,
239+
isHex ? NumberStyles.HexNumber : NumberStyles.Integer,
240+
CultureInfo.InvariantCulture,
241+
out value
242+
);
243+
}
193244
}

FModel/Views/Snooper/Options.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,18 @@ public void AnimateMesh(bool value)
240240
Services.ApplicationService.ApplicationView.CUE4Parse.ModelIsWaitingAnimation = value;
241241
}
242242

243+
/// <summary>
244+
/// Skip emmisive for specific games, cause of excessive use in their materials
245+
/// </summary>
246+
public bool SkipEmmisive()
247+
{
248+
return _game switch
249+
{
250+
"LIESOFP" => true,
251+
_ => false,
252+
};
253+
}
254+
243255
public void ResetModelsLightsAnimations()
244256
{
245257
foreach (var model in Models.Values)

0 commit comments

Comments
 (0)