support for multiple images in one package

This commit is contained in:
iAmAsval
2021-12-23 22:29:34 +01:00
parent dabe13d495
commit 05208deac7
5 changed files with 171 additions and 108 deletions

View File

@@ -94,6 +94,10 @@ namespace FModel
}
else if (_applicationView.IsReady && e.Key == Key.F && Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
OnSearchViewClick(null, null);
else if (e.Key == Key.Left)
_applicationView.CUE4Parse.TabControl.SelectedTab.GoPreviousImage();
else if (e.Key == Key.Right)
_applicationView.CUE4Parse.TabControl.SelectedTab.GoNextImage();
else if (UserSettings.Default.AssetAddTab.IsTriggered(e.Key))
_applicationView.CUE4Parse.TabControl.AddTab();
else if (UserSettings.Default.AssetRemoveTab.IsTriggered(e.Key))

View File

@@ -494,6 +494,7 @@ namespace FModel.ViewModels
TabControl.SelectedTab.Directory = directory;
}
TabControl.SelectedTab.ClearImages();
TabControl.SelectedTab.ResetDocumentText();
TabControl.SelectedTab.ScrollTrigger = null;
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(ext);
@@ -504,9 +505,13 @@ namespace FModel.ViewModels
{
var exports = Provider.LoadObjectExports(fullPath);
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), bulkSave);
if (bulkSave) break;
if (bulkSave || !exports.Any(CheckExport))
TabControl.SelectedTab.Image = null;
foreach (var e in exports)
{
if (CheckExport(e))
break;
}
break;
}
case "ini":
@@ -529,7 +534,6 @@ namespace FModel.ViewModels
case "archive":
case "manifest":
{
TabControl.SelectedTab.Image = null;
if (Provider.TrySaveAsset(fullPath, out var data))
{
using var stream = new MemoryStream(data) {Position = 0};
@@ -541,7 +545,6 @@ namespace FModel.ViewModels
}
case "locmeta":
{
TabControl.SelectedTab.Image = null;
if (Provider.TryCreateReader(fullPath, out var archive))
{
var metadata = new FTextLocalizationMetaDataResource(archive);
@@ -551,7 +554,6 @@ namespace FModel.ViewModels
}
case "locres":
{
TabControl.SelectedTab.Image = null;
if (Provider.TryCreateReader(fullPath, out var archive))
{
var locres = new FTextLocalizationResource(archive);
@@ -561,7 +563,6 @@ namespace FModel.ViewModels
}
case "bin" when fileName.Contains("AssetRegistry"):
{
TabControl.SelectedTab.Image = null;
if (Provider.TryCreateReader(fullPath, out var archive))
{
var registry = new FAssetRegistryState(archive);
@@ -572,7 +573,6 @@ namespace FModel.ViewModels
case "bnk":
case "pck":
{
TabControl.SelectedTab.Image = null;
if (Provider.TryCreateReader(fullPath, out var archive))
{
var wwise = new WwiseReader(archive);
@@ -586,7 +586,6 @@ namespace FModel.ViewModels
}
case "wem":
{
TabControl.SelectedTab.Image = null;
if (Provider.TrySaveAsset(fullPath, out var input))
SaveAndPlaySound(fullPath, "WEM", input);
@@ -594,7 +593,6 @@ namespace FModel.ViewModels
}
case "udic":
{
TabControl.SelectedTab.Image = null;
if (Provider.TryCreateReader(fullPath, out var archive))
{
var header = new FOodleDictionaryArchive(archive).Header;
@@ -609,7 +607,7 @@ namespace FModel.ViewModels
if (Provider.TrySaveAsset(fullPath, out var data))
{
using var stream = new MemoryStream(data) {Position = 0};
TabControl.SelectedTab.SetImage(SKImage.FromBitmap(SKBitmap.Decode(stream)));
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, SKImage.FromBitmap(SKBitmap.Decode(stream)));
}
break;
}
@@ -619,7 +617,7 @@ namespace FModel.ViewModels
{
using var stream = new MemoryStream(data) { Position = 0 };
var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512));
TabControl.SelectedTab.SetImage(SKImage.FromPicture(svg.Load(stream), new SKSizeI(512, 512)));
TabControl.SelectedTab.AddImage(fileName.SubstringBeforeLast("."), false, SKImage.FromPicture(svg.Load(stream), new SKSizeI(512, 512)));
}
break;
}
@@ -632,7 +630,6 @@ namespace FModel.ViewModels
case "ushaderbytecode":
case "ushadercode":
{
TabControl.SelectedTab.Image = null;
if (Provider.TryCreateReader(fullPath, out var archive))
{
var ar = new FShaderCodeArchive(archive);
@@ -659,8 +656,11 @@ namespace FModel.ViewModels
TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(""); // json
TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), false);
if (!exports.Any(CheckExport))
TabControl.SelectedTab.Image = null;
foreach (var e in exports)
{
if (CheckExport(e))
break;
}
}
private bool CheckExport(UObject export) // return true once you wanna stop searching for exports
@@ -669,9 +669,8 @@ namespace FModel.ViewModels
{
case UTexture2D texture:
{
TabControl.SelectedTab.RenderNearestNeighbor = texture.bRenderNearestNeighbor;
TabControl.SelectedTab.SetImage(texture.Decode());
return true;
TabControl.SelectedTab.AddImage(texture);
return false;
}
case UAkMediaAssetData:
case USoundWave:
@@ -722,7 +721,7 @@ namespace FModel.ViewModels
if (!package.TryConstructCreator(out var creator)) return false;
creator.ParseForInfo();
TabControl.SelectedTab.SetImage(creator.Draw());
TabControl.SelectedTab.AddImage(export.Name, false, creator.Draw());
return true;
}
}

View File

@@ -23,23 +23,23 @@ namespace FModel.ViewModels.Commands
{
case "Open":
{
Helper.OpenWindow<AdonisWindow>(contextViewModel.Header + " (Image)", () =>
Helper.OpenWindow<AdonisWindow>(contextViewModel.SelectedImage.ExportName + " (Image)", () =>
{
var popout = new ImagePopout
{
Title = contextViewModel.Header + " (Image)",
Width = contextViewModel.Image.Width,
Height = contextViewModel.Image.Height,
WindowState = contextViewModel.Image.Height > 1000 ? WindowState.Maximized : WindowState.Normal,
ImageCtrl = {Source = contextViewModel.Image}
Title = contextViewModel.SelectedImage.ExportName + " (Image)",
Width = contextViewModel.SelectedImage.Image.Width,
Height = contextViewModel.SelectedImage.Image.Height,
WindowState = contextViewModel.SelectedImage.Image.Height > 1000 ? WindowState.Maximized : WindowState.Normal,
ImageCtrl = {Source = contextViewModel.SelectedImage.Image}
};
RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, BoolToRenderModeConverter.Instance.Convert(contextViewModel.RenderNearestNeighbor));
RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, BoolToRenderModeConverter.Instance.Convert(contextViewModel.SelectedImage.RenderNearestNeighbor));
popout.Show();
});
break;
}
case "Copy":
ClipboardExtensions.SetImage(contextViewModel.ImageBuffer, Path.ChangeExtension(contextViewModel.Header, ".png"));
ClipboardExtensions.SetImage(contextViewModel.SelectedImage.ImageBuffer, $"{contextViewModel.SelectedImage.ExportName}.png");
break;
case "Save":
contextViewModel.SaveImage(false);
@@ -47,4 +47,4 @@ namespace FModel.ViewModels.Commands
}
}
}
}
}

View File

@@ -15,9 +15,71 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Media.Imaging;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse_Conversion.Textures;
namespace FModel.ViewModels
{
public class TabImage : ViewModel
{
public string ExportName { get; }
public byte[] ImageBuffer { get; set; }
public TabImage(string name, bool rnn, SKImage img)
{
ExportName = name;
RenderNearestNeighbor = rnn;
SetImage(img);
}
private BitmapImage _image;
public BitmapImage Image
{
get => _image;
set
{
if (_image == value) return;
SetProperty(ref _image, value);
}
}
private bool _renderNearestNeighbor;
public bool RenderNearestNeighbor
{
get => _renderNearestNeighbor;
set => SetProperty(ref _renderNearestNeighbor, value);
}
private bool _noAlpha;
public bool NoAlpha
{
get => _noAlpha;
set
{
SetProperty(ref _noAlpha, value);
ResetImage();
}
}
private void SetImage(SKImage img)
{
_img = img;
using var data = _img.Encode(NoAlpha ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100);
using var stream = new MemoryStream(ImageBuffer = data.ToArray(), false);
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
Image = image;
}
private SKImage _img;
private void ResetImage() => SetImage(_img);
}
public class TabItem : ViewModel
{
private string _header;
@@ -115,39 +177,25 @@ namespace FModel.ViewModels
}
}
public byte[] ImageBuffer { get; private set; }
private BitmapImage _image;
public BitmapImage Image
private TabImage _selectedImage;
public TabImage SelectedImage
{
get => _image;
get => _selectedImage;
set
{
if (_image == value) return;
SetProperty(ref _image, value);
if (_selectedImage == value) return;
SetProperty(ref _selectedImage, value);
RaisePropertyChanged("HasImage");
RaisePropertyChanged("Page");
}
}
private bool _noAlpha;
public bool NoAlpha
{
get => _noAlpha;
set
{
SetProperty(ref _noAlpha, value);
ResetImage();
}
}
public bool HasImage => SelectedImage != null;
public bool HasMultipleImages => _images.Count > 1;
public string Page => $"{_images.IndexOf(_selectedImage) + 1} / {_images.Count}";
private bool _renderNearestNeighbor;
public bool RenderNearestNeighbor
{
get => _renderNearestNeighbor;
set => SetProperty(ref _renderNearestNeighbor, value);
}
private readonly ObservableCollection<TabImage> _images;
public bool HasImage => Image != null;
public bool ShouldScroll => !string.IsNullOrEmpty(ScrollTrigger);
private TabCommand _tabCommand;
@@ -161,8 +209,37 @@ namespace FModel.ViewModels
{
Header = header;
Directory = directory;
_images = new ObservableCollection<TabImage>();
}
public void ClearImages()
{
Application.Current.Dispatcher.Invoke(() =>
{
_images.Clear();
SelectedImage = null;
});
}
public void AddImage(UTexture2D texture) => AddImage(texture.Name, texture.bRenderNearestNeighbor, texture.Decode());
public void AddImage(string name, bool rnn, SKImage img)
{
Application.Current.Dispatcher.Invoke(() =>
{
var t = new TabImage(name, rnn, img);
if (UserSettings.Default.IsAutoSaveTextures)
SaveImage(t, true);
_images.Add(t);
SelectedImage ??= t;
RaisePropertyChanged("Page");
RaisePropertyChanged("HasMultipleImages");
});
}
public void GoPreviousImage() => SelectedImage = _images.Previous(SelectedImage);
public void GoNextImage() => SelectedImage = _images.Next(SelectedImage);
public void SetDocumentText(string text, bool bulkSave)
{
Application.Current.Dispatcher.Invoke(() =>
@@ -174,7 +251,6 @@ namespace FModel.ViewModels
SaveProperty(true);
});
}
public void ResetDocumentText()
{
Application.Current.Dispatcher.Invoke(() =>
@@ -184,6 +260,40 @@ namespace FModel.ViewModels
});
}
public void SaveImage(bool autoSave) => SaveImage(SelectedImage, autoSave);
private void SaveImage(TabImage image, bool autoSave)
{
if (image == null) return;
var fileName = $"{image.ExportName}.png";
var directory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports",
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName!).Replace('\\', '/');
if (!autoSave)
{
var saveFileDialog = new SaveFileDialog
{
Title = "Save Texture",
FileName = fileName,
InitialDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports"),
Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*"
};
var result = saveFileDialog.ShowDialog();
if (!result.HasValue || !result.Value) return;
directory = saveFileDialog.FileName;
}
else
{
System.IO.Directory.CreateDirectory(directory.SubstringBeforeLast('/'));
}
using (var fs = new FileStream(directory, FileMode.Create, FileAccess.Write, FileShare.Read))
{
fs.Write(image.ImageBuffer, 0, image.ImageBuffer.Length);
}
SaveCheck(directory, fileName);
}
public void SaveProperty(bool autoSave)
{
var fileName = Path.ChangeExtension(Header, ".json");
@@ -212,60 +322,7 @@ namespace FModel.ViewModels
SaveCheck(directory, fileName);
}
private SKImage _img;
public void ResetImage() => SetImage(_img);
public void SetImage(SKImage img)
{
_img = img;
using var data = _img.Encode(NoAlpha ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100);
using var stream = new MemoryStream(ImageBuffer = data.ToArray(), false);
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
Image = image;
if (UserSettings.Default.IsAutoSaveTextures)
SaveImage(true);
}
public void SaveImage(bool autoSave)
{
if (!HasImage) return;
var fileName = Path.ChangeExtension(Header, ".png");
var directory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports",
UserSettings.Default.KeepDirectoryStructure ? Directory : "", fileName!).Replace('\\', '/');
if (!autoSave)
{
var saveFileDialog = new SaveFileDialog
{
Title = "Save Texture",
FileName = fileName,
InitialDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports"),
Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*"
};
var result = saveFileDialog.ShowDialog();
if (!result.HasValue || !result.Value) return;
directory = saveFileDialog.FileName;
}
else
{
System.IO.Directory.CreateDirectory(directory.SubstringBeforeLast('/'));
}
using (var fs = new FileStream(directory, FileMode.Create, FileAccess.Write, FileShare.Read))
{
fs.Write(ImageBuffer, 0, ImageBuffer.Length);
}
SaveCheck(directory, fileName);
}
private static void SaveCheck(string path, string fileName)
private void SaveCheck(string path, string fileName)
{
if (File.Exists(path))
{

View File

@@ -764,13 +764,13 @@
<controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" Grid.ColumnSpan="3" DataContext="{Binding SelectedItem, ElementName=TabControlName}" />
<Image Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform"
Visibility="{Binding SelectedItem.HasImage, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
Source="{Binding SelectedItem.Image, ElementName=TabControlName}" RenderOptions.BitmapScalingMode="{Binding SelectedItem.RenderNearestNeighbor, ElementName=TabControlName, Converter={x:Static converters:BoolToRenderModeConverter.Instance}}">
Source="{Binding SelectedItem.SelectedImage.Image, ElementName=TabControlName}" RenderOptions.BitmapScalingMode="{Binding SelectedItem.SelectedImage.RenderNearestNeighbor, ElementName=TabControlName, Converter={x:Static converters:BoolToRenderModeConverter.Instance}}">
<Image.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding SelectedItem.ImageCommand, ElementName=TabControlName}" CommandParameter="Open"/>
</Image.InputBindings>
<Image.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Disable Alpha Channel" IsCheckable="True" IsChecked="{Binding DataContext.NoAlpha}" />
<MenuItem Header="Disable Alpha Channel" IsCheckable="True" IsChecked="{Binding DataContext.SelectedImage.NoAlpha}" />
<MenuItem Header="Open Image" Command="{Binding DataContext.ImageCommand}" CommandParameter="Open">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
@@ -803,6 +803,9 @@
</ContextMenu>
</Image.ContextMenu>
</Image>
<TextBlock Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Bottom" HorizontalAlignment="Center"
Visibility="{Binding SelectedItem.HasMultipleImages, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
Text="{Binding SelectedItem.Page, ElementName=TabControlName}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding SelectedItem.HasImage, ElementName=TabControlName}" Value="False">