mirror of
https://github.com/4sval/FModel.git
synced 2026-05-05 20:36:46 -05:00
Roco Kingdom: World lua support, Neverness to Everness ini decryption
This commit is contained in:
parent
c279f1fd9c
commit
9159a91626
|
|
@ -1 +1 @@
|
|||
Subproject commit 3ea8a56be6e16012ebdeaab6e17f7b14049abda4
|
||||
Subproject commit 81458ae77d3f3230d7582a77ffd938510430a4bf
|
||||
|
|
@ -164,3 +164,9 @@ public enum EAssetCategory : uint
|
|||
RocoKingdomWorld = GameSpecific + 3,
|
||||
DeltaForce = GameSpecific + 4,
|
||||
}
|
||||
|
||||
public enum EUnluacMode
|
||||
{
|
||||
Decompile,
|
||||
Disassemble,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,8 @@ public partial class MainWindow
|
|||
{
|
||||
if (UserSettings.Default.DiscordRpc == EDiscordRpc.Always)
|
||||
_discordHandler.Initialize(_applicationView.GameDisplayName);
|
||||
})
|
||||
}),
|
||||
UserSettings.Default.DecompileLua ? ApplicationViewModel.InitUnluac() : Task.CompletedTask
|
||||
).ConfigureAwait(false);
|
||||
|
||||
#if DEBUG
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ public class DirectorySettings : ViewModel, ICloneable
|
|||
Directories = old?.Directories ?? CustomDirectory.Default(gameName),
|
||||
AesKeys = old?.AesKeys ?? new AesResponse { MainKey = aes, DynamicKeys = null },
|
||||
LastAesReload = old?.LastAesReload ?? DateTime.Today.AddDays(-1),
|
||||
CriwareDecryptionKey = old?.CriwareDecryptionKey ?? 0
|
||||
CriwareDecryptionKey = old?.CriwareDecryptionKey ?? 0,
|
||||
UnluacOpCodeMap = old?.UnluacOpCodeMap ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +107,13 @@ public class DirectorySettings : ViewModel, ICloneable
|
|||
set => SetProperty(ref _criwareDecryptionKey, value);
|
||||
}
|
||||
|
||||
private string _unluacOpCodeMap;
|
||||
public string UnluacOpCodeMap
|
||||
{
|
||||
get => _unluacOpCodeMap;
|
||||
set => SetProperty(ref _unluacOpCodeMap, value);
|
||||
}
|
||||
|
||||
private bool Equals(DirectorySettings other)
|
||||
{
|
||||
return GameDirectory == other.GameDirectory && UeVersion == other.UeVersion;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using CUE4Parse_Conversion.Animations;
|
|||
using CUE4Parse_Conversion.Meshes;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using CUE4Parse_Conversion.UEFormat.Enums;
|
||||
using CUE4Parse.UE4.Lua.unluac;
|
||||
using FModel.Framework;
|
||||
using FModel.ViewModels;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
|
|
@ -280,6 +281,36 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _convertAudioOnBulkExport, value);
|
||||
}
|
||||
|
||||
private bool _decompileLua;
|
||||
public bool DecompileLua
|
||||
{
|
||||
get => _decompileLua;
|
||||
set => SetProperty(ref _decompileLua, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public EUnluacMode UnluacMode
|
||||
{
|
||||
get => UnluacFlags.HasFlag(EUnluacFlags.Disassemble) ? EUnluacMode.Disassemble : EUnluacMode.Decompile;
|
||||
set
|
||||
{
|
||||
var withoutMode = UnluacFlags & ~(EUnluacFlags.Decompile | EUnluacFlags.Disassemble);
|
||||
var modeFlag = value == EUnluacMode.Disassemble ? EUnluacFlags.Disassemble : EUnluacFlags.Decompile;
|
||||
UnluacFlags = withoutMode | modeFlag;
|
||||
}
|
||||
}
|
||||
|
||||
private EUnluacFlags _unluacFlags;
|
||||
public EUnluacFlags UnluacFlags
|
||||
{
|
||||
get => _unluacFlags;
|
||||
set
|
||||
{
|
||||
if (!SetProperty(ref _unluacFlags, value)) return;
|
||||
RaisePropertyChanged(nameof(UnluacMode));
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<string, DirectorySettings> _perDirectory = new Dictionary<string, DirectorySettings>();
|
||||
public IDictionary<string, DirectorySettings> PerDirectory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Windows;
|
|||
using CUE4Parse_Conversion.Textures.BC;
|
||||
using CUE4Parse.Compression;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
using CUE4Parse.UE4.Lua.unluac;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using CUE4Parse.UE4.VirtualFileSystem;
|
||||
using FModel.Extensions;
|
||||
|
|
@ -340,4 +341,12 @@ public class ApplicationViewModel : ViewModel
|
|||
|
||||
DetexHelper.Initialize(detexPath);
|
||||
}
|
||||
|
||||
public static async Task InitUnluac()
|
||||
{
|
||||
var unluacPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", UnluacHelper.DllName);
|
||||
await UnluacHelper.InitializeAsync(unluacPath).ConfigureAwait(false);
|
||||
if (UnluacHelper.Instance is null)
|
||||
FLogger.Append(ELog.Error, () => FLogger.Text("Failed to download unluac", Constants.WHITE, true));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ public class AudioPlayerViewModel : ViewModel, ISource, IDisposable
|
|||
|
||||
if (Spectrum != null && PlayedFile.PlaybackState == PlaybackState.Playing)
|
||||
{
|
||||
FftData = new float[4096];
|
||||
FftData = new float[4096+4];
|
||||
Spectrum.GetFftData(FftData);
|
||||
RaiseSourcePropertyChangedEvent(ESourceProperty.FftData, FftData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,12 +43,14 @@ using CUE4Parse.UE4.Assets.Exports.StaticMesh;
|
|||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Exports.Verse;
|
||||
using CUE4Parse.UE4.Assets.Exports.Wwise;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
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;
|
||||
using CUE4Parse.UE4.Lua.unluac;
|
||||
using CUE4Parse.UE4.Objects.Core.Serialization;
|
||||
using CUE4Parse.UE4.Objects.Engine;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
|
|
@ -698,6 +700,18 @@ public class CUE4ParseViewModel : ViewModel
|
|||
ProcessCacheDBFile(entry, updateUi, saveProperties);
|
||||
break;
|
||||
}
|
||||
case "luac":
|
||||
case "lua":
|
||||
{
|
||||
var data = Provider.SaveAsset(entry);
|
||||
byte[] decompiled = ProcessLuaFile(data);
|
||||
|
||||
using var stream = new MemoryStream(decompiled);
|
||||
using var reader = new StreamReader(stream);
|
||||
TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi);
|
||||
|
||||
break;
|
||||
}
|
||||
case "upluginmanifest":
|
||||
case "code-workspace":
|
||||
case "projectstore":
|
||||
|
|
@ -749,7 +763,6 @@ public class CUE4ParseViewModel : ViewModel
|
|||
case "apx":
|
||||
case "udn":
|
||||
case "doc":
|
||||
case "lua":
|
||||
case "vdf":
|
||||
case "yml":
|
||||
case "js":
|
||||
|
|
@ -843,7 +856,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
case "pck":
|
||||
{
|
||||
var archive = entry.CreateReader();
|
||||
var wwise = new WwiseReader(archive, new WwiseGameFileSource(entry));
|
||||
var wwise = new WwiseReader(new FWwiseArchive(archive), new WwiseGameFileSource(entry));
|
||||
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi);
|
||||
|
||||
var medias = WwiseProvider.ExtractBankSounds(wwise);
|
||||
|
|
@ -982,7 +995,6 @@ public class CUE4ParseViewModel : ViewModel
|
|||
break;
|
||||
}
|
||||
case "res": // just skip
|
||||
case "luac": // compiled lua
|
||||
case "bytes": // wuthering waves
|
||||
break;
|
||||
default:
|
||||
|
|
@ -1088,6 +1100,53 @@ public class CUE4ParseViewModel : ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
private byte[] ProcessLuaFile(byte[] data)
|
||||
{
|
||||
var result = EUnluacErrorCode.Ok;
|
||||
byte[] output = [];
|
||||
if (BitConverter.ToUInt32(data) == UnluacHelper.LuaMagic && UnluacHelper.Instance is not null)
|
||||
{
|
||||
// opcodemap patch
|
||||
byte[] opmapData = Provider.Versions.Game switch
|
||||
{
|
||||
_ => [],
|
||||
};
|
||||
|
||||
var flags = UserSettings.Default.UnluacFlags;
|
||||
var opcodemap = UserSettings.Default.CurrentDir.UnluacOpCodeMap;
|
||||
if (!string.IsNullOrWhiteSpace(opcodemap))
|
||||
{
|
||||
opmapData = Encoding.UTF8.GetBytes(opcodemap);
|
||||
flags |= EUnluacFlags.OpCodeMap;
|
||||
}
|
||||
else if (opmapData is { Length: > 12 })
|
||||
{
|
||||
flags |= EUnluacFlags.OpCodeMapPatch;
|
||||
}
|
||||
|
||||
result = UnluacHelper.Decompile(data, opmapData, (uint)flags, out output, out var log);
|
||||
if (result != EUnluacErrorCode.Ok && log.Length > 0)
|
||||
{
|
||||
Log.Error(Encoding.UTF8.GetString(log));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = EUnluacErrorCode.Error;
|
||||
}
|
||||
|
||||
var decompiled = result switch
|
||||
{
|
||||
EUnluacErrorCode.Ok => output,
|
||||
#if DEBUG
|
||||
EUnluacErrorCode.PartialDecompile => output,
|
||||
#endif
|
||||
_ => data,
|
||||
};
|
||||
|
||||
return decompiled;
|
||||
}
|
||||
|
||||
public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName, string parentExportType)
|
||||
{
|
||||
Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath);
|
||||
|
|
|
|||
|
|
@ -172,6 +172,13 @@ public class SettingsViewModel : ViewModel
|
|||
set => SetProperty(ref _criwareDecryptionKey, value);
|
||||
}
|
||||
|
||||
private string _unluacOpcodeMap;
|
||||
public string UnluacOpcodeMap
|
||||
{
|
||||
get => _unluacOpcodeMap;
|
||||
set => SetProperty(ref _unluacOpcodeMap, value);
|
||||
}
|
||||
|
||||
public bool SocketSettingsEnabled => SelectedMeshExportFormat == EMeshFormat.ActorX;
|
||||
public bool CompressionSettingsEnabled => SelectedMeshExportFormat == EMeshFormat.UEFormat;
|
||||
|
||||
|
|
@ -237,6 +244,7 @@ public class SettingsViewModel : ViewModel
|
|||
_optionsSnapshot = UserSettings.Default.CurrentDir.Versioning.Options;
|
||||
_mapStructTypesSnapshot = UserSettings.Default.CurrentDir.Versioning.MapStructTypes;
|
||||
_criwareDecryptionKey = UserSettings.Default.CurrentDir.CriwareDecryptionKey;
|
||||
_unluacOpcodeMap = UserSettings.Default.CurrentDir.UnluacOpCodeMap;
|
||||
|
||||
AesEndpoint = UserSettings.Default.CurrentDir.Endpoints[0];
|
||||
MappingEndpoint = UserSettings.Default.CurrentDir.Endpoints[1];
|
||||
|
|
@ -273,6 +281,7 @@ public class SettingsViewModel : ViewModel
|
|||
SelectedMaterialExportFormat = _materialExportFormatSnapshot;
|
||||
SelectedTextureExportFormat = _textureExportFormatSnapshot;
|
||||
CriwareDecryptionKey = _criwareDecryptionKey;
|
||||
UnluacOpcodeMap = _unluacOpcodeMap;
|
||||
SelectedAesReload = UserSettings.Default.AesReload;
|
||||
SelectedDiscordRpc = UserSettings.Default.DiscordRpc;
|
||||
|
||||
|
|
@ -314,6 +323,7 @@ public class SettingsViewModel : ViewModel
|
|||
UserSettings.Default.CurrentDir.Versioning.Options = SelectedOptions;
|
||||
UserSettings.Default.CurrentDir.Versioning.MapStructTypes = SelectedMapStructTypes;
|
||||
UserSettings.Default.CurrentDir.CriwareDecryptionKey = CriwareDecryptionKey;
|
||||
UserSettings.Default.CurrentDir.UnluacOpCodeMap = UnluacOpcodeMap;
|
||||
|
||||
UserSettings.Default.AssetLanguage = SelectedAssetLanguage;
|
||||
UserSettings.Default.CompressedAudioMode = SelectedCompressedAudio;
|
||||
|
|
|
|||
32
FModel/Views/Resources/Converters/EnumFlagToBoolConverter.cs
Normal file
32
FModel/Views/Resources/Converters/EnumFlagToBoolConverter.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FModel.Views.Resources.Converters;
|
||||
|
||||
public sealed class EnumFlagToBoolConverter : IValueConverter
|
||||
{
|
||||
public static readonly EnumFlagToBoolConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is null || parameter is null) return false;
|
||||
|
||||
var enumType = value.GetType();
|
||||
if (!enumType.IsEnum) return false;
|
||||
|
||||
var flag = parameter is string s
|
||||
? Enum.Parse(enumType, s, ignoreCase: true)
|
||||
: parameter;
|
||||
|
||||
var current = System.Convert.ToInt64(value);
|
||||
var wanted = System.Convert.ToInt64(flag);
|
||||
|
||||
return (current & wanted) == wanted;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
|
@ -237,26 +238,32 @@
|
|||
IsChecked="{Binding ShowDecompileOption, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||
|
||||
<TextBlock Grid.Row="19"
|
||||
<TextBlock Grid.Row="19" Grid.Column="0" Text="Decompile Lua" VerticalAlignment="Center" Margin="0 0 0 5" ToolTip="Decompile/Disassemble compiled lua files with an unluac." />
|
||||
<CheckBox Grid.Row="19" Grid.Column="2" Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
IsChecked="{Binding DecompileLua, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" Margin="0 5 0 10"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}"
|
||||
Checked="OnDecompileLuaChanged" />
|
||||
|
||||
<TextBlock Grid.Row="20"
|
||||
Grid.Column="0"
|
||||
Text="Convert Audio During Export (.wav)"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 0 5" />
|
||||
<CheckBox Grid.Row="19"
|
||||
<CheckBox Grid.Row="20"
|
||||
Grid.Column="2"
|
||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
IsChecked="{Binding ConvertAudioOnBulkExport, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||
Margin="0 5 0 10"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" />
|
||||
|
||||
<TextBlock Grid.Row="20"
|
||||
<TextBlock Grid.Row="21"
|
||||
Grid.Column="0"
|
||||
Text="CRIWARE Decryption Key"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 0 10" />
|
||||
|
||||
<TextBox x:Name="CriwareKeyBox"
|
||||
Grid.Row="20"
|
||||
Grid.Row="21"
|
||||
Grid.Column="2"
|
||||
Grid.ColumnSpan="5"
|
||||
Margin="0 5 0 10"
|
||||
|
|
@ -604,6 +611,98 @@
|
|||
HotKey="{Binding NextAudio, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="unluacTemplate">
|
||||
<Grid adonisExtensions:LayerExtension.Layer="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="5" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Mode" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="10"
|
||||
SelectedValuePath="Tag"
|
||||
SelectedValue="{Binding UnluacMode, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"
|
||||
Margin="0 0 0 5">
|
||||
<ComboBoxItem Content="Decompile" Tag="{x:Static local:EUnluacMode.Decompile}" />
|
||||
<ComboBoxItem Content="Disassemble" Tag="{x:Static local:EUnluacMode.Disassemble}" />
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Raw string" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<CheckBox Tag="RawString"
|
||||
Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="3"
|
||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" Margin="0 5 0 5"
|
||||
IsChecked="{Binding UnluacFlags, Source={x:Static local:Settings.UserSettings.Default},
|
||||
Converter={x:Static converters:EnumFlagToBoolConverter.Instance}, ConverterParameter=RawString, Mode=OneWay}"
|
||||
Checked="OnUnluacFlagChanged"
|
||||
Unchecked="OnUnluacFlagChanged"
|
||||
ToolTip="Copy string bytes directly to output">
|
||||
</CheckBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="4" Text="No debug" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<CheckBox Tag="NoDebug"
|
||||
Grid.Row="1" Grid.Column="6" Grid.ColumnSpan="3"
|
||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" Margin="0 5 0 5"
|
||||
IsChecked="{Binding UnluacFlags, Source={x:Static local:Settings.UserSettings.Default},
|
||||
Converter={x:Static converters:EnumFlagToBoolConverter.Instance}, ConverterParameter=NoDebug, Mode=OneWay}"
|
||||
Checked="OnUnluacFlagChanged"
|
||||
Unchecked="OnUnluacFlagChanged"
|
||||
ToolTip="Ignore debugging information in input file">
|
||||
</CheckBox>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="8" Text="Luaj" VerticalAlignment="Center" Margin="0 0 0 5" />
|
||||
<CheckBox Tag="Luaj"
|
||||
Grid.Row="1" Grid.Column="10" Grid.ColumnSpan="3"
|
||||
Content="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={x:Static converters:BoolToToggleConverter.Instance}}"
|
||||
Style="{DynamicResource {x:Static adonisUi:Styles.ToggleSwitch}}" Margin="0 5 0 5"
|
||||
IsChecked="{Binding UnluacFlags, Source={x:Static local:Settings.UserSettings.Default},
|
||||
Converter={x:Static converters:EnumFlagToBoolConverter.Instance}, ConverterParameter=Luaj, Mode=OneWay}"
|
||||
Checked="OnUnluacFlagChanged"
|
||||
Unchecked="OnUnluacFlagChanged"
|
||||
ToolTip="Emulate Luaj's permissive parser">
|
||||
</CheckBox>
|
||||
|
||||
<Separator Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="11" Style="{StaticResource CustomSeparator}" />
|
||||
|
||||
<TextBlock Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Text="OpcodeMap"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0 0 0 5"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="10"
|
||||
Margin="0 0 0 5" Height="300"
|
||||
FontSize="14" Padding="8"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top"
|
||||
VerticalContentAlignment="Top" TextAlignment="Left"
|
||||
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Views.SettingsView}}}"
|
||||
Text="{Binding SettingsView.UnluacOpcodeMap, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
TextWrapping="NoWrap"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
ToolTip="Paste a custom opcode map." />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</adonisControls:AdonisWindow.Resources>
|
||||
<Grid>
|
||||
|
|
@ -681,6 +780,28 @@
|
|||
</StackPanel>
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem Tag="unluacTemplate">
|
||||
<TreeViewItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox Width="16" Height="16" HorizontalAlignment="Center" Margin="-20 4 7.5 4">
|
||||
<Canvas Width="24" Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource LuaIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
<TextBlock Text="unluac" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TreeViewItem.Header>
|
||||
<TreeViewItem.Style>
|
||||
<Style TargetType="TreeViewItem" BasedOn="{StaticResource TreeViewItemStyle}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding DecompileLua, Source={x:Static local:Settings.UserSettings.Default}}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TreeViewItem.Style>
|
||||
</TreeViewItem>
|
||||
</TreeView>
|
||||
|
||||
<Grid Grid.Row="0" Grid.Column="1" Margin="{adonisUi:Space 1, 0.5}" HorizontalAlignment="Stretch">
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using CUE4Parse.UE4.Lua.unluac;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
|
|
@ -274,4 +275,21 @@ public partial class SettingsView
|
|||
|
||||
Process.Start(new ProcessStartInfo(hyperlink.NavigateUri.AbsoluteUri) { UseShellExecute = true });
|
||||
}
|
||||
|
||||
private async void OnDecompileLuaChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is CheckBox { IsChecked: true } && UnluacHelper.Instance is null)
|
||||
await ApplicationViewModel.InitUnluac();
|
||||
}
|
||||
|
||||
private void OnUnluacFlagChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not CheckBox cb || cb.Tag is not string name) return;
|
||||
if (!Enum.TryParse<EUnluacFlags>(name, true, out var flag)) return;
|
||||
|
||||
var current = UserSettings.Default.UnluacFlags;
|
||||
var isChecked = cb.IsChecked == true;
|
||||
|
||||
UserSettings.Default.UnluacFlags = isChecked ? (current | flag) : (current & ~flag);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user