mirror of
https://github.com/4sval/FModel.git
synced 2026-09-13 12:06:39 -05:00
support for multiple images in one package
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user