Add built-in ELF viewer plugin

Support for binary ELF, UImage, Mach-O files
This commit is contained in:
ema
2025-06-22 20:47:29 +08:00
parent 17091056de
commit f0d55455e4
18 changed files with 1334 additions and 1 deletions

View File

@@ -0,0 +1,144 @@
<UserControl x:Class="QuickLook.Plugin.ELFViewer.InfoPanels.ELFInfoPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:QuickLook.Plugin.ELFViewer.InfoPanels"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="192"
FontSize="14"
UseLayoutRounding="True"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="15" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="image"
Grid.Row="1"
Grid.Column="0"
Width="128"
Height="128"
Margin="0,-20,0,0"
Opacity="0"
Stretch="Fill">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source, ElementName=image}" Value="{x:Null}">
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
MaxHeight="60"
Padding="3"
FontSize="19"
FontWeight="SemiBold"
LineHeight="25"
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap">
<TextBlock.Inlines>
<Run x:Name="filename" Text="FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext" />
<InlineUIContainer>
<local:FluentBorder x:Name="architectureContainer"
Background="#0067C0"
CornerRadius="5"
Visibility="Collapsed">
<TextBlock x:Name="architecture"
Height="20"
Padding="6,0,6,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="15"
Foreground="White" />
</local:FluentBorder>
</InlineUIContainer>
</TextBlock.Inlines>
</TextBlock>
</Grid>
<!-- Total Size -->
<TextBlock x:Name="totalSizeTitle"
Grid.Row="3"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Total Size" />
<TextBlock x:Name="totalSize"
Grid.Row="3"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Calculating size..." />
<!-- Format -->
<TextBlock x:Name="formatTitle"
Grid.Row="4"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Format" />
<TextBlock x:Name="format"
Grid.Row="4"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis" />
<!-- Format Profile -->
<TextBlock x:Name="formatProfileTitle"
Grid.Row="5"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Format Profile" />
<TextBlock x:Name="formatProfile"
Grid.Row="5"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap" />
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,238 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using ELFSharp.ELF;
using QuickLook.Common.ExtensionMethods;
using QuickLook.Common.Helpers;
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace QuickLook.Plugin.ELFViewer.InfoPanels;
public partial class ELFInfoPanel : UserControl, IInfoPanel
{
public ELFInfoPanel()
{
InitializeComponent();
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
formatTitle.Text = TranslationHelper.Get("FORMAT", translationFile);
formatProfileTitle.Text = TranslationHelper.Get("FORMAT_PROFILE", translationFile);
}
public void DisplayInfo(string path)
{
var name = Path.GetFileName(path);
filename.Text = string.IsNullOrEmpty(name) ? path : name;
_ = Task.Run(() =>
{
if (File.Exists(path))
{
var size = new FileInfo(path).Length;
var isElf = ELFReader.TryLoad(path, out var elf);
var arch = elf?.Machine.ToMachineName();
var typeLogo = elf?.Type switch
{
FileType.Executable => "exec",
FileType.SharedObject => "dyn",
FileType.None or FileType.Relocatable or FileType.Core or _ => "none",
};
Dispatcher.Invoke(() =>
{
architectureContainer.Visibility = string.IsNullOrEmpty(arch) ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
architecture.Text = arch;
format.Text = "ELF";
formatProfile.Text = elf != null ? $"{elf.Type} / {elf.Machine}" : "NotELF";
totalSize.Text = size.ToPrettySize(2);
image.Source = new BitmapImage(new Uri($"pack://application:,,,/QuickLook.Plugin.ELFViewer;component/Resources/{typeLogo}.png"));
});
}
});
}
}
file static class MachineTypeExtension
{
public static string ToMachineName(this Machine machine)
{
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
return machine switch
{
Machine.M32 => "M32",
Machine.SPARC => "SPARC",
Machine.Intel386 => "x86",
Machine.M68K => "M68K",
Machine.M88K => "M88K",
Machine.Intel486 => "Intel486",
Machine.Intel860 => "Intel860",
Machine.MIPS => "MIPS",
Machine.S370 => "S370",
Machine.MIPSRS3LE => "MIPSRS3LE",
Machine.PARISC => "PA-RISC",
Machine.VPP500 => "VPP500",
Machine.SPARC32Plus => "SPARCv8+",
Machine.Intel960 => "Intel960",
Machine.PPC => "PowerPC",
Machine.PPC64 => "PowerPC64",
Machine.S390 => "S390",
Machine.SPU => "SPU",
Machine.V800 => "V800",
Machine.FR20 => "FR20",
Machine.RH32 => "RH-32",
Machine.RCE => "RCE",
Machine.ARM => "ARM",
Machine.Alpha => "Alpha",
Machine.SuperH => "SuperH",
Machine.SPARCv9 => "SPARCv9",
Machine.TriCore => "TriCore",
Machine.ARC => "Argonaut RISC Core",
Machine.H8300 => "H8300",
Machine.H8300H => "H8300H",
Machine.H8S => "H8S",
Machine.H8500 => "H8500",
Machine.IA64 => "IA64",
Machine.MIPSX => "MIPSX",
Machine.ColdFire => "ColdFire",
Machine.M68HC12 => "M68HC12",
Machine.MMA => "MMA",
Machine.PCP => "PCP",
Machine.NCPU => "RISC",
Machine.NDR1 => "NDR1",
Machine.StarCore => "Star*Core",
Machine.ME16 => "ME16",
Machine.ST100 => "ST100",
Machine.TinyJ => "TinyJ",
Machine.AMD64 => "x64",
Machine.PDSP => "PDSP",
Machine.PDP10 => "PDP10",
Machine.PDP11 => "PDP11",
Machine.FX66 => "FX66",
Machine.ST9PLUS => "ST9PLUS",
Machine.ST7 => "ST7",
Machine.M68HC16 => "M68HC16",
Machine.M68HC11 => "M68HC11",
Machine.M68HC08 => "M68HC08",
Machine.M68HC05 => "M68HC05",
Machine.SVX => "SVx",
Machine.ST19 => "ST19",
Machine.VAX => "VAX",
Machine.CRIS => "CRIS",
Machine.Javelin => "Javelin",
Machine.FirePath => "FirePath",
Machine.ZSP => "ZSP",
Machine.MMIX => "MMIX",
Machine.HUANY => "HUANY",
Machine.PRISM => "PRISM",
Machine.AVR => "AVR",
Machine.FR30 => "FR30",
Machine.D10V => "D10V",
Machine.D30V => "D30V",
Machine.V850 => "V850",
Machine.M32R => "M32R",
Machine.MN10300 => "MN10300",
Machine.MN10200 => "MN10200",
Machine.PicoJava => "PicoJava",
Machine.OpenRISC => "OpenRISC",
Machine.ARCompact => "ARCompact",
Machine.Xtensa => "Xtensa",
Machine.VideoCore => "VideoCore",
Machine.TMMGPP => "TMMGPP",
Machine.NS32K => "NS32K",
Machine.TPC => "TPC",
Machine.SNP1k => "SNP1k",
Machine.ST200 => "ST200",
Machine.IP2K => "IP2K",
Machine.MAX => "MAX",
Machine.CompactRISC => "CompactRISC",
Machine.F2MC16 => "F2MC16",
Machine.MSP430 => "MSP430",
Machine.Blackfin => "Blackfin",
Machine.S1C33 => "S1C33",
Machine.SEP => "SEP",
Machine.ArcaRISC => "ArcaRISC",
Machine.UNICORE => "UNICORE",
Machine.Excess => "Excess",
Machine.DXP => "DXP",
Machine.AlteraNios2 => "AlteraNios2",
Machine.CRX => "CRX",
Machine.XGATE => "XGATE",
Machine.C166 => "C166",
Machine.M16C => "M16C",
Machine.DSPIC30F => "DSPIC30F",
Machine.EngineRISC => "EngineRISC",
Machine.M32C => "M32C",
Machine.TSK3000 => "TSK3000",
Machine.RS08 => "RS08",
Machine.SHARC => "SHARC",
Machine.ECOG2 => "ECOG2",
Machine.Score7 => "Score7",
Machine.DSP24 => "DSP24",
Machine.VideoCore3 => "VideoCore3",
Machine.LatticeMico32 => "LatticeMico32",
Machine.SeikoEpsonC17 => "SeikoEpsonC17",
Machine.TIC6000 => "TIC6000",
Machine.TIC2000 => "TIC2000",
Machine.TIC5500 => "TIC5500",
Machine.MMDSPPlus => "MMDSPPlus",
Machine.CypressM8C => "CypressM8C",
Machine.R32C => "R32C",
Machine.TriMedia => "TriMedia",
Machine.Hexagon => "Hexagon",
Machine.Intel8051 => "Intel8051",
Machine.STxP7x => "STxP7x",
Machine.NDS32 => "NDS32",
Machine.ECOG1 or Machine.ECOG1X => "ECOG1",
Machine.MAXQ30 => "MAXQ30",
Machine.XIMO16 => "XIMO16",
Machine.MANIK => "MANIK",
Machine.CrayNV2 => "CrayNV2",
Machine.RX => "RX",
Machine.METAG => "METAG",
Machine.MCSTElbrus => "MCSTElbrus",
Machine.ECOG16 => "ECOG16",
Machine.CR16 => "CR16",
Machine.ETPU => "ETPU",
Machine.SLE9X => "SLE9X",
Machine.L10M => "L10M",
Machine.K10M => "K10M",
Machine.AArch64 => "ARM64",
Machine.AVR32 => "AVR32",
Machine.STM8 => "STM8",
Machine.TILE64 => "TILE64",
Machine.TILEPro => "TILEPro",
Machine.CUDA => "CUDA",
Machine.TILEGx => "TILEGx",
Machine.CloudShield => "CloudShield",
Machine.CoreA1st => "CoreA1st",
Machine.CoreA2nd => "CoreA2nd",
Machine.ARCompact2 => "ARCompactV2",
Machine.Open8 => "Open8",
Machine.RL78 => "RL78",
Machine.VideoCore5 => "VideoCore5",
Machine.R78KOR => "R78KOR",
Machine.F56800EX => "F56800EX",
Machine.None or _ => "UNKNOWN",
};
}
}

View File

@@ -0,0 +1,52 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace QuickLook.Plugin.ELFViewer.InfoPanels;
public class FluentBorder : Decorator
{
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(FluentBorder), new FrameworkPropertyMetadata(new CornerRadius()));
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
public static readonly DependencyProperty BackgroundProperty =
DependencyProperty.Register("Background", typeof(Brush), typeof(FluentBorder), new FrameworkPropertyMetadata(Brushes.Transparent));
public Brush Background
{
get => (Brush)GetValue(BackgroundProperty);
set => SetValue(BackgroundProperty, value);
}
protected override void OnRender(DrawingContext drawingContext)
{
if (Child != null)
{
Rect rect = new(new Point(0, 0), RenderSize);
drawingContext.DrawRoundedRectangle(Background, new Pen(Brushes.Transparent, 1), rect, CornerRadius.TopLeft, CornerRadius.TopRight);
}
}
}

View File

@@ -0,0 +1,23 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace QuickLook.Plugin.ELFViewer.InfoPanels;
public interface IInfoPanel
{
public void DisplayInfo(string path);
}

View File

@@ -0,0 +1,144 @@
<UserControl x:Class="QuickLook.Plugin.ELFViewer.InfoPanels.MachOInfoPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:QuickLook.Plugin.ELFViewer.InfoPanels"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="192"
FontSize="14"
UseLayoutRounding="True"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="15" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="image"
Grid.Row="1"
Grid.Column="0"
Width="128"
Height="128"
Margin="0,-20,0,0"
Opacity="0"
Stretch="Fill">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source, ElementName=image}" Value="{x:Null}">
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
MaxHeight="60"
Padding="3"
FontSize="19"
FontWeight="SemiBold"
LineHeight="25"
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap">
<TextBlock.Inlines>
<Run x:Name="filename" Text="FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext" />
<InlineUIContainer>
<local:FluentBorder x:Name="architectureContainer"
Background="#0067C0"
CornerRadius="5"
Visibility="Collapsed">
<TextBlock x:Name="architecture"
Height="20"
Padding="6,0,6,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="15"
Foreground="White" />
</local:FluentBorder>
</InlineUIContainer>
</TextBlock.Inlines>
</TextBlock>
</Grid>
<!-- Total Size -->
<TextBlock x:Name="totalSizeTitle"
Grid.Row="3"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Total Size" />
<TextBlock x:Name="totalSize"
Grid.Row="3"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Calculating size..." />
<!-- Format -->
<TextBlock x:Name="formatTitle"
Grid.Row="4"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Format" />
<TextBlock x:Name="format"
Grid.Row="4"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis" />
<!-- Format Profile -->
<TextBlock x:Name="formatProfileTitle"
Grid.Row="5"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Format Profile" />
<TextBlock x:Name="formatProfile"
Grid.Row="5"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap" />
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,109 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using ELFSharp.MachO;
using QuickLook.Common.ExtensionMethods;
using QuickLook.Common.Helpers;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace QuickLook.Plugin.ELFViewer.InfoPanels;
public partial class MachOInfoPanel : UserControl, IInfoPanel
{
public MachOInfoPanel()
{
InitializeComponent();
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
formatTitle.Text = TranslationHelper.Get("FORMAT", translationFile);
formatProfileTitle.Text = TranslationHelper.Get("FORMAT_PROFILE", translationFile);
}
public void DisplayInfo(string path)
{
var name = Path.GetFileName(path);
filename.Text = string.IsNullOrEmpty(name) ? path : name;
_ = Task.Run(() =>
{
if (File.Exists(path))
{
var size = new FileInfo(path).Length;
var tried = MachOReader.TryLoad(path, out MachO machO);
var arch = string.Empty;
var profile = string.Empty;
if (tried == MachOResult.OK)
{
arch = machO.Machine.ToMachineName();
profile = machO.Machine.ToString();
}
else if (tried == MachOResult.FatMachO)
{
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
if (MachOReader.TryLoadFat(stream, shouldOwnStream: true, out var machOs) == MachOResult.FatMachO)
{
arch = string.Join(", ", machOs.Select(m => m.Machine.ToMachineName()).Distinct());
profile = string.Join(", ", machOs.Select(m => m.Machine.ToString()).Distinct());
}
}
Dispatcher.Invoke(() =>
{
architectureContainer.Visibility = string.IsNullOrEmpty(arch) ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
architecture.Text = arch;
format.Text = "DynamicLibrary";
formatProfile.Text = profile;
totalSize.Text = size.ToPrettySize(2);
image.Source = new BitmapImage(new Uri("pack://application:,,,/QuickLook.Plugin.ELFViewer;component/Resources/dyn.png"));
});
}
});
}
}
file static class MachineTypeExtension
{
public static string ToMachineName(this Machine machine)
{
return machine switch
{
Machine.Vax => "Vax",
Machine.M68k => "M68k",
Machine.X86 => "x86",
Machine.X86_64 => "x64",
Machine.M98k => "M98k",
Machine.PaRisc => "PaRisc",
Machine.Arm => "Arm",
Machine.Arm64 => "Arm64",
Machine.M88k => "M88k",
Machine.Sparc => "Sparc",
Machine.I860 => "I860",
Machine.PowerPc => "PowerPc",
Machine.PowerPc64 => "PowerPc64",
Machine.Any or _ => "UNKNOWN",
};
}
}

View File

@@ -0,0 +1,159 @@
<UserControl x:Class="QuickLook.Plugin.ELFViewer.InfoPanels.UImageInfoPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:QuickLook.Plugin.ELFViewer.InfoPanels"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="192"
FontSize="14"
UseLayoutRounding="True"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="15" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="image"
Grid.Row="1"
Grid.Column="0"
Width="128"
Height="128"
Margin="0,-20,0,0"
Opacity="0"
Stretch="Fill">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source, ElementName=image}" Value="{x:Null}">
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.05" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
MaxHeight="60"
Padding="3"
FontSize="19"
FontWeight="SemiBold"
LineHeight="25"
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap">
<TextBlock.Inlines>
<Run x:Name="filename" Text="FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext" />
<InlineUIContainer>
<local:FluentBorder x:Name="architectureContainer"
Background="#0067C0"
CornerRadius="5"
Visibility="Collapsed">
<TextBlock x:Name="architecture"
Height="20"
Padding="6,0,6,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="15"
Foreground="White" />
</local:FluentBorder>
</InlineUIContainer>
</TextBlock.Inlines>
</TextBlock>
</Grid>
<!-- Name -->
<TextBlock x:Name="imageNameTitle"
Grid.Row="3"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Name" />
<TextBlock x:Name="imageName"
Grid.Row="3"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..." />
<!-- Total Size -->
<TextBlock x:Name="totalSizeTitle"
Grid.Row="4"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Total Size" />
<TextBlock x:Name="totalSize"
Grid.Row="4"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Calculating size..." />
<!-- Format -->
<TextBlock x:Name="formatTitle"
Grid.Row="5"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Format" />
<TextBlock x:Name="format"
Grid.Row="5"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis" />
<!-- Format Profile -->
<TextBlock x:Name="formatProfileTitle"
Grid.Row="6"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Format Profile" />
<TextBlock x:Name="formatProfile"
Grid.Row="6"
Grid.Column="2"
Margin="8,0,0,0"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap" />
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,112 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using ELFSharp.UImage;
using QuickLook.Common.ExtensionMethods;
using QuickLook.Common.Helpers;
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace QuickLook.Plugin.ELFViewer.InfoPanels;
public partial class UImageInfoPanel : UserControl, IInfoPanel
{
public UImageInfoPanel()
{
InitializeComponent();
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
imageNameTitle.Text = TranslationHelper.Get("NAME", translationFile);
totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
formatTitle.Text = TranslationHelper.Get("FORMAT", translationFile);
formatProfileTitle.Text = TranslationHelper.Get("FORMAT_PROFILE", translationFile);
}
public void DisplayInfo(string path)
{
var name = Path.GetFileName(path);
filename.Text = string.IsNullOrEmpty(name) ? path : name;
_ = Task.Run(() =>
{
if (File.Exists(path))
{
var size = new FileInfo(path).Length;
var tried = UImageReader.TryLoad(path, out UImage uImage);
Dispatcher.Invoke(() =>
{
var arch = tried == UImageResult.OK ? uImage.Architecture.ToMachineName() : string.Empty;
architectureContainer.Visibility = string.IsNullOrEmpty(arch) ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
if (tried == UImageResult.OK)
{
imageName.Text = uImage.Name;
architecture.Text = arch;
format.Text = $"UImage - {uImage.OperatingSystem} / {uImage.Compression}";
formatProfile.Text = $"{uImage.Type} / {uImage.Architecture}";
}
else
{
imageName.Text = string.Empty;
architecture.Text = string.Empty;
format.Text = tried.ToString();
formatProfile.Text = tried.ToString();
}
totalSize.Text = size.ToPrettySize(2);
image.Source = new BitmapImage(new Uri("pack://application:,,,/QuickLook.Plugin.ELFViewer;component/Resources/uboot.png"));
});
}
});
}
}
file static class MachineTypeExtension
{
public static string ToMachineName(this Architecture arch)
{
return arch switch
{
Architecture.Alpha => "Alpha",
Architecture.ARM => "ARM",
Architecture.Ix86 => "Ix86",
Architecture.Itanium => "Itanium",
Architecture.MIPS => "MIPS",
Architecture.MIPS64 => "MIPS64",
Architecture.PowerPC => "PowerPC",
Architecture.S390 => "S390",
Architecture.SuperH => "SuperH",
Architecture.SPARC => "SPARC",
Architecture.SPARC64 => "SPARC64",
Architecture.M68k => "M68k",
Architecture.MicroBlaze => "MicroBlaze",
Architecture.Nios2 => "Nios2",
Architecture.Blackfin => "Blackfin",
Architecture.AVR32 => "AVR32",
Architecture.ST200 => "ST200",
Architecture.Sandbox => "Sandbox",
Architecture.NDS32 => "NDS32",
Architecture.OpenRISC => "OpenRISC",
Architecture.Invalid or _ => "UNKNOWN",
};
}
}

View File

@@ -0,0 +1,150 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using ELFSharp.ELF;
using ELFSharp.MachO;
using ELFSharp.UImage;
using QuickLook.Common.Plugin;
using QuickLook.Plugin.ELFViewer.InfoPanels;
using System;
using System.IO;
using System.Linq;
using System.Windows;
namespace QuickLook.Plugin.ELFViewer;
public class Plugin : IViewer
{
/// <summary>
/// Magic number of ELF files
/// 0x7F 'E' 'L' 'F'
/// </summary>
private static readonly byte[] _magic = [0x7F, 0x45, 0x4C, 0x46];
private static readonly string[] _extensions =
[
".axf", ".bin", ".elf", ".o", ".out", ".prx", ".puff", ".ko", ".mod", "so",
];
private IInfoPanel _ip;
private string _path;
private FileEnum _type;
public int Priority => 11;
public void Init()
{
}
public bool CanHandle(string path)
{
return DetectFormat(path) != FileEnum.None;
}
public void Prepare(string path, ContextObject context)
{
context.PreferredSize = new Size { Width = 520, Height = 192 };
context.Title = string.Empty;
context.TitlebarOverlap = false;
context.TitlebarBlurVisibility = false;
context.TitlebarColourVisibility = false;
context.FullWindowDragging = true;
}
public void View(string path, ContextObject context)
{
_path = path;
_type = DetectFormat(path);
_ip = _type switch
{
FileEnum.ELF => new ELFInfoPanel(),
FileEnum.MachO => new MachOInfoPanel(),
FileEnum.UImage => new UImageInfoPanel(),
_ => throw new NotImplementedException(),
};
_ip.DisplayInfo(_path);
context.ViewerContent = _ip;
context.IsBusy = false;
}
public void Cleanup()
{
GC.SuppressFinalize(this);
_ip = null;
}
private static FileEnum DetectFormat(string path)
{
if (Directory.Exists(path))
return FileEnum.None;
var pathLower = Path.GetFileName(path).ToLower();
var extension = Path.GetExtension(pathLower);
// UImage
if (pathLower.EndsWith(".uimage"))
{
return FileEnum.UImage;
}
else if (pathLower.Equals("uimage"))
{
if (UImageReader.TryLoad(path, out _) == UImageResult.OK)
return FileEnum.UImage;
}
// Mach-O
if (pathLower.EndsWith(".dylib"))
{
return FileEnum.MachO;
}
else if (extension == string.Empty)
{
if (MachOReader.TryLoad(path, out _) != MachOResult.NotMachO)
return FileEnum.MachO;
}
// ELF
if (_extensions.Any(pathLower.EndsWith) || extension == string.Empty)
{
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using var br = new BinaryReader(fs);
if (br.BaseStream.Length < Consts.MinimalELFSize)
return FileEnum.None;
var magic = br.ReadBytes(4);
for (var i = 0; i < 4; i++)
if (magic[i] != _magic[i])
return FileEnum.None;
return FileEnum.ELF;
}
return FileEnum.None;
}
private enum FileEnum
{
None,
ELF,
UImage,
MachO,
}
}

View File

@@ -0,0 +1,50 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("QuickLook.Plugin.ELFViewer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("pooi.moe")]
[assembly: AssemblyProduct("QuickLook.Plugin.ELFViewer")]
[assembly: AssemblyCopyright("Copyright © 2017-2025 QL-Win Contributors")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("63c00175-0ff3-42c7-8621-2f1959f26064")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]

View File

@@ -0,0 +1,83 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net462</TargetFramework>
<RootNamespace>QuickLook.Plugin.ELFViewer</RootNamespace>
<AssemblyName>QuickLook.Plugin.ELFViewer</AssemblyName>
<FileAlignment>512</FileAlignment>
<SignAssembly>false</SignAssembly>
<UseWPF>true</UseWPF>
<LangVersion>latest</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<ProjectGuid>{63C00175-0FF3-42C7-8621-2F1959F26064}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\Build\Debug\QuickLook.Plugin\QuickLook.Plugin.ELFViewer\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Build\Release\QuickLook.Plugin\QuickLook.Plugin.ELFViewer\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<OutputPath>..\..\Build\Debug\QuickLook.Plugin\QuickLook.Plugin.ELFViewer\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>..\..\Build\Release\QuickLook.Plugin\QuickLook.Plugin.ELFViewer\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\QuickLook.Common\QuickLook.Common.csproj">
<Project>{85FDD6BA-871D-46C8-BD64-F6BB0CB5EA95}</Project>
<Name>QuickLook.Common</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Remove="Resources\*.png" />
<Resource Include="Resources\*.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\GitVersion.cs">
<Link>Properties\GitVersion.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="ELFSharp" Version="2.17.3" />
</ItemGroup>
<ItemGroup>
<None Update="Translations.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<Translations>
<en>
<TOTAL_SIZE>Total Size</TOTAL_SIZE>
<FORMAT>Format</FORMAT>
<FORMAT_PROFILE>Format Profile</FORMAT_PROFILE>
<NAME>Name</NAME>
</en>
<pt-BR>
<TOTAL_SIZE>Tamanho total</TOTAL_SIZE>
<FORMAT>Formato</FORMAT>
<FORMAT_PROFILE>Perfil do Formato</FORMAT_PROFILE>
<NAME>Nome</NAME>
</pt-BR>
<zh-CN>
<TOTAL_SIZE>总大小</TOTAL_SIZE>
<FORMAT>格式</FORMAT>
<FORMAT_PROFILE>格式配置</FORMAT_PROFILE>
<NAME>名称</NAME>
</zh-CN>
<zh-TW>
<TOTAL_SIZE>縂大小</TOTAL_SIZE>
<FORMAT>格式</FORMAT>
<FORMAT_PROFILE>格式設定</FORMAT_PROFILE>
<NAME>名稱</NAME>
</zh-TW>
<ja>
<TOTAL_SIZE>合計サイズ</TOTAL_SIZE>
<FORMAT>フォーマット</FORMAT>
<FORMAT_PROFILE>フォーマットプロファイル</FORMAT_PROFILE>
<NAME>名称</NAME>
</ja>
<de>
<TOTAL_SIZE>Gesamtgröße</TOTAL_SIZE>
<FORMAT>Format</FORMAT>
<FORMAT_PROFILE>Formatprofil</FORMAT_PROFILE>
<NAME>Name</NAME>
</de>
</Translations>

View File

@@ -1,4 +1,21 @@
using System.Windows;
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

View File

@@ -53,6 +53,7 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "QuickLook.Installer", "Quic
{538FD6BA-2AF1-4DDA-93A2-6C0A12B00843} = {538FD6BA-2AF1-4DDA-93A2-6C0A12B00843}
{EB7D1F10-6E04-4F7E-96A0-4E18359FBD30} = {EB7D1F10-6E04-4F7E-96A0-4E18359FBD30}
{88B6FFC8-AD86-41D9-B03D-BD83886CFC18} = {88B6FFC8-AD86-41D9-B03D-BD83886CFC18}
{63C00175-0FF3-42C7-8621-2F1959F26064} = {63C00175-0FF3-42C7-8621-2F1959F26064}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QuickLook.Native64", "QuickLook.Native\QuickLook.Native64\QuickLook.Native64.vcxproj", "{794E4DCF-F715-4836-9D30-ABD296586D23}"
@@ -77,6 +78,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.AppViewer"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.OfficeViewer", "QuickLook.Plugin\QuickLook.Plugin.OfficeViewer\QuickLook.Plugin.OfficeViewer.csproj", "{88B6FFC8-AD86-41D9-B03D-BD83886CFC18}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.ELFViewer", "QuickLook.Plugin\QuickLook.Plugin.ELFViewer\QuickLook.Plugin.ELFViewer.csproj", "{63C00175-0FF3-42C7-8621-2F1959F26064}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -245,6 +248,14 @@ Global
{88B6FFC8-AD86-41D9-B03D-BD83886CFC18}.Release|Any CPU.Build.0 = Release|Any CPU
{88B6FFC8-AD86-41D9-B03D-BD83886CFC18}.Release|x64.ActiveCfg = Release|Any CPU
{88B6FFC8-AD86-41D9-B03D-BD83886CFC18}.Release|x64.Build.0 = Release|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Debug|x64.ActiveCfg = Debug|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Debug|x64.Build.0 = Debug|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Release|Any CPU.Build.0 = Release|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Release|x64.ActiveCfg = Release|Any CPU
{63C00175-0FF3-42C7-8621-2F1959F26064}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -267,6 +278,7 @@ Global
{538FD6BA-2AF1-4DDA-93A2-6C0A12B00843} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93}
{EB7D1F10-6E04-4F7E-96A0-4E18359FBD30} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93}
{88B6FFC8-AD86-41D9-B03D-BD83886CFC18} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93}
{63C00175-0FF3-42C7-8621-2F1959F26064} = {06EFDBE0-6408-4B37-BCF2-0CF9EBEA2E93}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D3761C32-8C5F-498A-892B-3B0882994B62}