// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // Some of the classes and functions in this file were found online. // Where possible the original authors are referenced. using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Threading; namespace WPinternals { internal delegate void SetWorkingStatus(string Message, string SubMessage = null, ulong? MaxProgressValue = null, bool ShowAnimation = true, WPinternalsStatus Status = WPinternalsStatus.Undefined); internal delegate void UpdateWorkingStatus(string Message, string SubMessage = null, ulong? CurrentProgressValue = null, WPinternalsStatus Status = WPinternalsStatus.Undefined); internal delegate void ExitSuccess(string Message, string SubMessage = null); internal delegate void ExitFailure(string Message, string SubMessage = null); internal enum WPinternalsStatus { Undefined, Scanning, Flashing, Patching, WaitingForManualReset, SwitchingMode, Initializing }; public class BooleanConverter : DependencyObject, IValueConverter { public static readonly DependencyProperty OnTrueProperty = DependencyProperty.Register("OnTrue", typeof(object), typeof(BooleanConverter), new PropertyMetadata(default(object))); public static readonly DependencyProperty OnFalseProperty = DependencyProperty.Register("OnFalse", typeof(object), typeof(BooleanConverter), new PropertyMetadata(default(object))); public static readonly DependencyProperty OnNullProperty = DependencyProperty.Register("OnNull", typeof(object), typeof(BooleanConverter), new PropertyMetadata(default(object))); public object OnTrue { get { return GetValue(OnTrueProperty); } set { SetValue(OnTrueProperty, value); } } public object OnFalse { get { return GetValue(OnFalseProperty); } set { SetValue(OnFalseProperty, value); } } public object OnNull { get { return GetValue(OnNullProperty); } set { SetValue(OnNullProperty, value); } } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value == null ? OnNull ?? Default(targetType) : (string.Equals(value.ToString(), false.ToString(), StringComparison.CurrentCultureIgnoreCase) ? OnFalse : OnTrue); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value == OnNull) { return Default(targetType); } if (value == OnFalse) { return false; } if (value == OnTrue) { return true; } if (value == null) { return null; } if (OnNull != null && string.Equals(value.ToString(), OnNull.ToString(), StringComparison.CurrentCultureIgnoreCase)) { return Default(targetType); } if (OnFalse != null && string.Equals(value.ToString(), OnFalse.ToString(), StringComparison.CurrentCultureIgnoreCase)) { return false; } if (OnTrue != null && string.Equals(value.ToString(), OnTrue.ToString(), StringComparison.CurrentCultureIgnoreCase)) { return true; } return null; } public static object Default(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } } public class HexConverter : DependencyObject, IValueConverter { public static readonly DependencyProperty SeparatorProperty = DependencyProperty.Register("OnTrue", typeof(object), typeof(HexConverter), new PropertyMetadata(" ")); public object Separator { get { return GetValue(SeparatorProperty); } set { SetValue(SeparatorProperty, value); } } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is byte[] bytes) { StringBuilder s = new(1000); for (int i = bytes.GetLowerBound(0); i <= bytes.GetUpperBound(0); i++) { if (i != bytes.GetLowerBound(0)) { s.Append(Separator); } s.Append(bytes[i].ToString("X2")); } return s.ToString(); } else { return ""; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public static object Default(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } } public class ObjectToVisibilityConverter : DependencyObject, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) { return "Collapsed"; } else { return "Visible"; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public static object Default(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } } public class InverseObjectToVisibilityConverter : DependencyObject, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) { return "Visible"; } else { return "Collapsed"; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public static object Default(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } } internal class CollapsibleRun : Run { private string CollapsibleText; protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); CollapsibleText = Text; } public Boolean IsVisible { get { return (Boolean)this.GetValue(IsVisibleProperty); } set { this.SetValue(IsVisibleProperty, value); } } public static readonly DependencyProperty IsVisibleProperty = DependencyProperty.Register( "IsVisible", typeof(Boolean), typeof(CollapsibleRun), new PropertyMetadata(true, IsVisibleChanged)); public static void IsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((CollapsibleRun)d).Text = (bool)e.NewValue ? ((CollapsibleRun)d).CollapsibleText : String.Empty; } } internal class CollapsibleSection : Section { public CollapsibleSection() { CollapsibleBlocks = new List(); } protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); foreach (Block Block in Blocks) { CollapsibleBlocks.Add(Block); } Blocks.Clear(); } public List CollapsibleBlocks { get; } public Boolean IsCollapsed { get { return (Boolean)this.GetValue(IsCollapsedProperty); } set { this.SetValue(IsCollapsedProperty, value); Blocks.Clear(); if (IsInitialized && !value) { foreach (Block Block in CollapsibleBlocks) { Blocks.Add(Block); } } } } public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register( "IsCollapsed", typeof(Boolean), typeof(CollapsibleSection), new PropertyMetadata(false)); } // Overloaded Paragraph class to remove empty Run-elements, caused by auto-formatting new-lines in the XAML // Use local:Paragraph in a FlowDocument // This correction only works at run-time public class Paragraph : System.Windows.Documents.Paragraph { protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); int inlinesCount = this.Inlines.Count; for (int i = 0; i < inlinesCount; i++) { Inline inline = this.Inlines.ElementAt(i); if (inline is Run run) { if (run.Text == Convert.ToChar(32).ToString()) //ACSII 32 is the white space { run.Text = string.Empty; } } } } } internal class ArrivalEventArgs : EventArgs { public PhoneInterfaces NewInterface; public IDisposable NewModel; public ArrivalEventArgs(PhoneInterfaces NewInterface, IDisposable NewModel) : base() { this.NewInterface = NewInterface; this.NewModel = NewModel; } } internal static class BigEndian { public static byte[] GetBytes(object Value) { byte[] Bytes; if (Value is short) { Bytes = BitConverter.GetBytes((short)Value); } else if (Value is ushort) { Bytes = BitConverter.GetBytes((ushort)Value); } else if (Value is int) { Bytes = BitConverter.GetBytes((int)Value); } else { Bytes = Value is uint ? BitConverter.GetBytes((uint)Value) : throw new NotSupportedException(); } byte[] Result = new byte[Bytes.Length]; for (int i = 0; i < Bytes.Length; i++) { Result[i] = Bytes[Bytes.Length - 1 - i]; } return Result; } public static byte[] GetBytes(object Value, int Width) { byte[] Result; byte[] BigEndianBytes = GetBytes(Value); if (BigEndianBytes.Length == Width) { return BigEndianBytes; } else if (BigEndianBytes.Length > Width) { Result = new byte[Width]; Buffer.BlockCopy(BigEndianBytes, BigEndianBytes.Length - Width, Result, 0, Width); return Result; } else { Result = new byte[Width]; Buffer.BlockCopy(BigEndianBytes, 0, Result, Width - BigEndianBytes.Length, BigEndianBytes.Length); return Result; } } public static UInt16 ToUInt16(byte[] Buffer, int Offset) { byte[] Bytes = new byte[2]; for (int i = 0; i < 2; i++) { Bytes[i] = Buffer[Offset + 1 - i]; } return BitConverter.ToUInt16(Bytes, 0); } public static Int16 ToInt16(byte[] Buffer, int Offset) { byte[] Bytes = new byte[2]; for (int i = 0; i < 2; i++) { Bytes[i] = Buffer[Offset + 1 - i]; } return BitConverter.ToInt16(Bytes, 0); } public static UInt32 ToUInt32(byte[] Buffer, int Offset) { byte[] Bytes = new byte[4]; for (int i = 0; i < 4; i++) { Bytes[i] = Buffer[Offset + 3 - i]; } return BitConverter.ToUInt32(Bytes, 0); } public static Int32 ToInt32(byte[] Buffer, int Offset) { byte[] Bytes = new byte[4]; for (int i = 0; i < 4; i++) { Bytes[i] = Buffer[Offset + 3 - i]; } return BitConverter.ToInt32(Bytes, 0); } } // This class was found online. // Original author is probably: mdm20 // https://stackoverflow.com/questions/5566330/get-gif-to-play-in-wpf-with-gifimage-class/5568703#5568703 internal class GifImage : Image { private bool _isInitialized; private GifBitmapDecoder _gifDecoder; private Int32Animation _animation; public int FrameIndex { get { return (int)GetValue(FrameIndexProperty); } set { SetValue(FrameIndexProperty, value); } } private void Initialize() { _gifDecoder = new GifBitmapDecoder(new Uri("pack://application:,,," + this.GifSource), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); _animation = new Int32Animation(0, _gifDecoder.Frames.Count - 1, new Duration(new TimeSpan(0, 0, 0, _gifDecoder.Frames.Count / 10, (int)(((_gifDecoder.Frames.Count / 10.0) - (_gifDecoder.Frames.Count / 10)) * 1000)))) { RepeatBehavior = RepeatBehavior.Forever }; this.Source = _gifDecoder.Frames[0]; _isInitialized = true; } static GifImage() { VisibilityProperty.OverrideMetadata(typeof(GifImage), new FrameworkPropertyMetadata(VisibilityPropertyChanged)); } private static void VisibilityPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if ((Visibility)e.NewValue == Visibility.Visible) { ((GifImage)sender).StartAnimation(); } else { ((GifImage)sender).StopAnimation(); } } public static readonly DependencyProperty FrameIndexProperty = DependencyProperty.Register("FrameIndex", typeof(int), typeof(GifImage), new UIPropertyMetadata(0, new PropertyChangedCallback(ChangingFrameIndex))); private static void ChangingFrameIndex(DependencyObject obj, DependencyPropertyChangedEventArgs ev) { var gifImage = obj as GifImage; gifImage.Source = gifImage._gifDecoder.Frames[(int)ev.NewValue]; } public bool AutoStart { get { return (bool)GetValue(AutoStartProperty); } set { SetValue(AutoStartProperty, value); } } public static readonly DependencyProperty AutoStartProperty = DependencyProperty.Register("AutoStart", typeof(bool), typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged)); private static void AutoStartPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { (sender as GifImage)?.StartAnimation(); } } public string GifSource { get { return (string)GetValue(GifSourceProperty); } set { SetValue(GifSourceProperty, value); } } public static readonly DependencyProperty GifSourceProperty = DependencyProperty.Register("GifSource", typeof(string), typeof(GifImage), new UIPropertyMetadata(string.Empty, GifSourcePropertyChanged)); private static void GifSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { (sender as GifImage)?.Initialize(); } public void StartAnimation() { if (!_isInitialized) { this.Initialize(); } BeginAnimation(FrameIndexProperty, _animation); } public void StopAnimation() { BeginAnimation(FrameIndexProperty, null); } } internal enum LogType { FileOnly, FileAndConsole, ConsoleOnly }; internal static class LogFile { private static readonly StreamWriter w = null; private static readonly object lockobject = new(); #if PREVIEW private static string LogAction = null; private static StringBuilder LogBuilder; #endif static LogFile() { try { if (!Directory.Exists(Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals"))) { Directory.CreateDirectory(Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals")); } w = File.AppendText(Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\WPInternals.log")); } catch { } } public static void Log(string logMessage, LogType Type = LogType.FileOnly) { if (w == null) { return; } lock (lockobject) { if ((Type == LogType.FileOnly) || (Type == LogType.FileAndConsole)) { DateTime Now = DateTime.Now; string Text = Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + ": " + logMessage; w.WriteLine(Text); w.Flush(); #if PREVIEW if (LogAction != null) LogBuilder.AppendLine(Text); #endif } if (CommandLine.IsConsoleVisible && ((Type == LogType.ConsoleOnly) || (Type == LogType.FileAndConsole))) { Console.WriteLine(logMessage); } } } public static void LogException(Exception Ex, LogType Type = LogType.FileAndConsole, string AdditionalInfo = null) { string Indent = ""; Exception CurrentEx = Ex; while (CurrentEx != null) { Log(Indent + "Error: " + RemoveBadChars(CurrentEx.Message).Replace("of type '.' ", "") + (AdditionalInfo == null ? "" : " - " + AdditionalInfo), Type); AdditionalInfo = null; if (CurrentEx is WPinternalsException) { Log(Indent + ((WPinternalsException)CurrentEx).SubMessage, Type); } #if DEBUG if (CurrentEx.StackTrace != null) { Log(Indent + CurrentEx.StackTrace, LogType.FileOnly); } #endif Indent += " "; CurrentEx = CurrentEx.InnerException; } } private static string RemoveBadChars(string Text) { return System.Text.RegularExpressions.Regex.Replace(Text, @"[^\u0020-\u007E]+", string.Empty); } public static void DumpLog(StreamReader r) { string line; while ((line = r.ReadLine()) != null) { Console.WriteLine(line); } } public static void LogApplicationVersion() { Log("Windows Phone Internals version " + Assembly.GetExecutingAssembly().GetName().Version.Major.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Build.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Revision.ToString(), LogType.FileAndConsole); Log("Copyright Heathcliff74", LogType.FileAndConsole); } internal static void BeginAction(string Action) { #if PREVIEW if (LogAction == null) { LogAction = Action; LogBuilder = new StringBuilder(); LogBuilder.AppendLine("Windows Phone Internals version " + Assembly.GetExecutingAssembly().GetName().Version.Major.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Build.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Revision.ToString()); LogBuilder.AppendLine("Copyright Heathcliff74"); LogBuilder.AppendLine("Action: " + Action); if (App.Config.RegistrationName != null) LogBuilder.AppendLine("Name: " + App.Config.RegistrationName); if (App.Config.RegistrationEmail != null) LogBuilder.AppendLine("Mail: " + App.Config.RegistrationEmail); if (App.Config.RegistrationSkypeID != null) LogBuilder.AppendLine("Skype: " + App.Config.RegistrationSkypeID); if (App.Config.RegistrationTelegramID != null) LogBuilder.AppendLine("Telegram: " + App.Config.RegistrationTelegramID); if (Environment.MachineName != null) LogBuilder.AppendLine("Machine: " + Environment.MachineName); } #endif } internal static void EndAction() { EndAction(null); } internal static void EndAction(string Action) { #if PREVIEW if ((LogAction != null) && ((Action == null) || (LogAction == Action))) { Action = LogAction; LogAction = null; string FileName = ""; if (App.Config.RegistrationName != null) FileName += App.Config.RegistrationName + " - "; FileName += DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + " - " + Action + " - " + Assembly.GetExecutingAssembly().GetName().Version.Major.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Build.ToString() + "." + Assembly.GetExecutingAssembly().GetName().Version.Revision.ToString() + ".log"; // Normalize filename try { FileName = System.Text.Encoding.ASCII.GetString(System.Text.Encoding.GetEncoding("ISO-8859-8").GetBytes(FileName)); } catch { } FileName = FileName.Replace("?", ""); if (Action.ToLower() == "registration") Uploader.Upload(FileName, LogBuilder.ToString()); else { try { Uploader.Upload(FileName, LogBuilder.ToString()); } catch { } } } #endif } } public static class Converter { public static string ConvertHexToString(byte[] Bytes, string Separator) { StringBuilder s = new(1000); for (int i = Bytes.GetLowerBound(0); i <= Bytes.GetUpperBound(0); i++) { if (i != Bytes.GetLowerBound(0)) { s.Append(Separator); } s.Append(Bytes[i].ToString("X2")); } return s.ToString(); } public static byte[] ConvertStringToHex(string HexString) { if (HexString.Length % 2 == 1) { throw new Exception("The binary key cannot have an odd number of digits"); } byte[] arr = new byte[HexString.Length >> 1]; for (int i = 0; i < (HexString.Length >> 1); ++i) { arr[i] = (byte)((GetHexVal(HexString[i << 1]) << 4) + GetHexVal(HexString[(i << 1) + 1])); } return arr; } public static int GetHexVal(char hex) { int val = hex; //For uppercase A-F letters: //return val - (val < 58 ? 48 : 55); //For lowercase a-f letters: //return val - (val < 58 ? 48 : 87); //Or the two combined, but a bit slower: return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); } } // This class was found online. // Original author is probably: John Melville // https://social.msdn.microsoft.com/Forums/en-US/163ef755-ff7b-4ea5-b226-bbe8ef5f4796/is-there-a-pattern-for-calling-an-async-method-synchronously?forum=async public static class AsyncHelpers { /// /// Execute's an async Task method which has a void return value synchronously /// /// Task method to execute public static void RunSync(Func task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); synch.Post(async _ => { try { await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } /// /// Execute's an async Task method which has a T return type synchronously /// /// Return Type /// Task method to execute /// public static T RunSync(Func> task) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default; synch.Post(async _ => { try { ret = await task(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } private class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } private readonly AutoResetEvent workItemsWaiting = new(false); private readonly Queue> items = new(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } workItemsWaiting.Set(); } public void EndMessageLoop() { Post(_ => done = true, null); } public void BeginMessageLoop() { while (!done) { Tuple task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) // the method threw an exeption { throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException); } } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } } // This class is taken from the Prism library by Microsoft Patterns & Practices // License: http://compositewpf.codeplex.com/license internal static class WeakEventHandlerManager { public static void AddWeakReferenceHandler(ref List handlers, EventHandler handler, int defaultListSize) { (handlers ??= (defaultListSize > 0) ? new List(defaultListSize) : new List()).Add(new WeakReference(handler)); } private static void CallHandler(object sender, EventHandler eventHandler) { DispatcherProxy proxy = DispatcherProxy.CreateDispatcher(); if (eventHandler != null) { if (proxy?.CheckAccess() == false) { proxy.BeginInvoke(new Action(CallHandler), new object[] { sender, eventHandler }); } else { eventHandler(sender, EventArgs.Empty); } } } public static void CallWeakReferenceHandlers(object sender, List handlers) { if (handlers != null) { EventHandler[] callees = new EventHandler[handlers.Count]; int count = 0; count = CleanupOldHandlers(handlers, callees, count); for (int i = 0; i < count; i++) { CallHandler(sender, callees[i]); } } } private static int CleanupOldHandlers(List handlers, EventHandler[] callees, int count) { for (int i = handlers.Count - 1; i >= 0; i--) { WeakReference reference = handlers[i]; if (reference.Target is not EventHandler target) { handlers.RemoveAt(i); } else { callees[count] = target; count++; } } return count; } public static void RemoveWeakReferenceHandler(List handlers, EventHandler handler) { if (handlers != null) { for (int i = handlers.Count - 1; i >= 0; i--) { WeakReference reference = handlers[i]; if ((reference.Target is not EventHandler target) || (target == handler)) { handlers.RemoveAt(i); } } } } private class DispatcherProxy { private readonly Dispatcher innerDispatcher; private DispatcherProxy(Dispatcher dispatcher) { this.innerDispatcher = dispatcher; } public DispatcherOperation BeginInvoke(Delegate method, params object[] args) { return this.innerDispatcher.BeginInvoke(method, DispatcherPriority.Normal, args); } public bool CheckAccess() { return this.innerDispatcher.CheckAccess(); } public static DispatcherProxy CreateDispatcher() { if (Application.Current == null) { return null; } return new DispatcherProxy(Application.Current.Dispatcher); } } } // This interface is taken from the Prism library by Microsoft Patterns & Practices // License: http://compositewpf.codeplex.com/license public interface IActiveAware { /// /// Gets or sets a value indicating whether the object is active. /// /// if the object is active; otherwise . bool IsActive { get; set; } /// /// Notifies that the value for property has changed. /// event EventHandler IsActiveChanged; } // This class is taken from the Prism library by Microsoft Patterns & Practices // License: http://compositewpf.codeplex.com/license public abstract class DelegateCommandBase : ICommand, IActiveAware { private List _canExecuteChangedHandlers; private bool _isActive; private readonly Func canExecuteMethod; private readonly Action executeMethod; public event EventHandler CanExecuteChanged { add { WeakEventHandlerManager.AddWeakReferenceHandler(ref this._canExecuteChangedHandlers, value, 2); } remove { WeakEventHandlerManager.RemoveWeakReferenceHandler(this._canExecuteChangedHandlers, value); } } public event EventHandler IsActiveChanged; protected DelegateCommandBase(Action executeMethod, Func canExecuteMethod) { if ((executeMethod == null) || (canExecuteMethod == null)) { throw new ArgumentNullException(nameof(executeMethod), "Delegate Command Delegates Cannot Be Null"); } this.executeMethod = executeMethod; this.canExecuteMethod = canExecuteMethod; } protected bool CanExecute(object parameter) { if (this.canExecuteMethod != null) { return this.canExecuteMethod(parameter); } return true; } protected void Execute(object parameter) { this.executeMethod(parameter); } protected virtual void OnCanExecuteChanged() { WeakEventHandlerManager.CallWeakReferenceHandlers(this, this._canExecuteChangedHandlers); } protected virtual void OnIsActiveChanged() { this.IsActiveChanged?.Invoke(this, EventArgs.Empty); } public void RaiseCanExecuteChanged() { this.OnCanExecuteChanged(); } bool ICommand.CanExecute(object parameter) { return this.CanExecute(parameter); } void ICommand.Execute(object parameter) { this.Execute(parameter); } public bool IsActive { get { return this._isActive; } set { if (this._isActive != value) { this._isActive = value; this.OnIsActiveChanged(); } } } } // This class is taken from the Prism library by Microsoft Patterns & Practices // License: http://compositewpf.codeplex.com/license public class DelegateCommand : DelegateCommandBase { public DelegateCommand(Action executeMethod) : this(executeMethod, () => true) { } public DelegateCommand(Action executeMethod, Func canExecuteMethod) : base(o => executeMethod(), f => canExecuteMethod()) { } public bool CanExecute() { return CanExecute(null); } public void Execute() { Execute(null); } } internal class FlowDocumentScrollViewerNoMouseWheel : FlowDocumentScrollViewer { protected override void OnMouseWheel(MouseWheelEventArgs e) { } } internal class ProgressUpdater { private readonly DateTime InitTime; private DateTime LastUpdateTime; private readonly UInt64 MaxValue; private readonly Action ProgressUpdateCallback; internal int ProgressPercentage; internal ProgressUpdater(UInt64 MaxValue, Action ProgressUpdateCallback) { InitTime = DateTime.Now; LastUpdateTime = DateTime.Now; this.MaxValue = MaxValue; this.ProgressUpdateCallback = ProgressUpdateCallback; SetProgress(0); } private UInt64 _Progress; internal UInt64 Progress { get { return _Progress; } } internal void SetProgress(UInt64 NewValue) { if (_Progress != NewValue) { int PreviousProgressPercentage = (int)((double)_Progress / MaxValue * 100); ProgressPercentage = (int)((double)NewValue / MaxValue * 100); _Progress = NewValue; if (((DateTime.Now - LastUpdateTime) > TimeSpan.FromSeconds(0.5)) || (ProgressPercentage == 100)) { #if DEBUG Console.WriteLine("Init time: " + InitTime.ToShortTimeString() + " / Now: " + DateTime.Now.ToString() + " / NewValue: " + NewValue.ToString() + " / MaxValue: " + MaxValue.ToString() + " ->> Percentage: " + ProgressPercentage.ToString() + " / Remaining: " + TimeSpan.FromTicks((long)((DateTime.Now - InitTime).Ticks / ((double)NewValue / MaxValue) * (1 - ((double)NewValue / MaxValue)))).ToString()); #endif if (((DateTime.Now - InitTime) < TimeSpan.FromSeconds(30)) && (ProgressPercentage < 15)) { ProgressUpdateCallback(ProgressPercentage, null); } else { ProgressUpdateCallback(ProgressPercentage, TimeSpan.FromTicks((long)((DateTime.Now - InitTime).Ticks / ((double)NewValue / MaxValue) * (1 - ((double)NewValue / MaxValue))))); } LastUpdateTime = DateTime.Now; } } } internal void IncreaseProgress(UInt64 Progress) { SetProgress(_Progress + Progress); } } internal static class Compression { internal static Stream GetDecompressedStreamWithSeek(Stream InputStream) { long P = InputStream.Position; byte[] GZipHeader = new byte[3]; InputStream.Read(GZipHeader, 0, 3); InputStream.Position = P; if (StructuralComparisons.StructuralEqualityComparer.Equals(GZipHeader, new byte[] { 0x1F, 0x8B, 0x08 })) { return new GZipStream(InputStream, CompressionMode.Decompress, false); } else { return InputStream; } } internal static bool IsCompressedStream(Stream InputStream) { byte[] GZipHeader = new byte[3]; InputStream.Read(GZipHeader, 0, 3); return StructuralComparisons.StructuralEqualityComparer.Equals(GZipHeader, new byte[] { 0x1F, 0x8B, 0x08 }); } internal static GZipStream GetDecompressedStream(Stream InputStream) { return new GZipStream(InputStream, CompressionMode.Decompress, false); } } internal class WPinternalsException : Exception { // Message and SubMessaage are always printable internal string SubMessage = null; internal WPinternalsException() : base() { } internal WPinternalsException(string Message) : base(Message) { } internal WPinternalsException(string Message, Exception InnerException) : base(Message, InnerException) { } internal WPinternalsException(string Message, string SubMessage) : base(Message) { this.SubMessage = SubMessage; } internal WPinternalsException(string Message, string SubMessage, Exception InnerException) : base(Message, InnerException) { this.SubMessage = SubMessage; } } // This class is written by: Eugene Beresovsky // https://stackoverflow.com/questions/13035925/stream-wrapper-to-make-stream-seekable/28036366#28036366 internal class ReadSeekableStream : Stream { private long _underlyingPosition; private readonly byte[] _seekBackBuffer; private int _seekBackBufferCount; private int _seekBackBufferIndex; private readonly Stream _underlyingStream; public ReadSeekableStream(Stream underlyingStream, int seekBackBufferSize) { if (!underlyingStream.CanRead) { throw new Exception("Provided stream " + underlyingStream + " is not readable"); } _underlyingStream = underlyingStream; _seekBackBuffer = new byte[seekBackBufferSize]; } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override int Read(byte[] buffer, int offset, int count) { int copiedFromBackBufferCount = 0; if (_seekBackBufferIndex < _seekBackBufferCount) { copiedFromBackBufferCount = Math.Min(count, _seekBackBufferCount - _seekBackBufferIndex); Buffer.BlockCopy(_seekBackBuffer, _seekBackBufferIndex, buffer, offset, copiedFromBackBufferCount); offset += copiedFromBackBufferCount; count -= copiedFromBackBufferCount; _seekBackBufferIndex += copiedFromBackBufferCount; } int bytesReadFromUnderlying = 0; if (count > 0) { bytesReadFromUnderlying = _underlyingStream.Read(buffer, offset, count); if (bytesReadFromUnderlying > 0) { _underlyingPosition += bytesReadFromUnderlying; var copyToBufferCount = Math.Min(bytesReadFromUnderlying, _seekBackBuffer.Length); var copyToBufferOffset = Math.Min(_seekBackBufferCount, _seekBackBuffer.Length - copyToBufferCount); var bufferBytesToMove = Math.Min(_seekBackBufferCount - 1, copyToBufferOffset); if (bufferBytesToMove > 0) { Buffer.BlockCopy(_seekBackBuffer, _seekBackBufferCount - bufferBytesToMove, _seekBackBuffer, 0, bufferBytesToMove); } Buffer.BlockCopy(buffer, offset, _seekBackBuffer, copyToBufferOffset, copyToBufferCount); _seekBackBufferCount = Math.Min(_seekBackBuffer.Length, _seekBackBufferCount + copyToBufferCount); _seekBackBufferIndex = _seekBackBufferCount; } } return copiedFromBackBufferCount + bytesReadFromUnderlying; } public override long Seek(long offset, SeekOrigin origin) { if (origin == SeekOrigin.End) { return SeekFromEnd((int)Math.Max(0, -offset)); } var relativeOffset = origin == SeekOrigin.Current ? offset : offset - Position; if (relativeOffset == 0) { return Position; } else if (relativeOffset > 0) { return SeekForward(relativeOffset); } else { return SeekBackwards(-relativeOffset); } } private long SeekForward(long origOffset) { long offset = origOffset; var seekBackBufferLength = _seekBackBuffer.Length; int backwardSoughtBytes = _seekBackBufferCount - _seekBackBufferIndex; int seekForwardInBackBuffer = (int)Math.Min(offset, backwardSoughtBytes); offset -= seekForwardInBackBuffer; _seekBackBufferIndex += seekForwardInBackBuffer; if (offset > 0) { // first completely fill seekBackBuffer to remove special cases from while loop below if (_seekBackBufferCount < seekBackBufferLength) { var maxRead = seekBackBufferLength - _seekBackBufferCount; if (offset < maxRead) { maxRead = (int)offset; } var bytesRead = _underlyingStream.Read(_seekBackBuffer, _seekBackBufferCount, maxRead); _underlyingPosition += bytesRead; _seekBackBufferCount += bytesRead; _seekBackBufferIndex = _seekBackBufferCount; if (bytesRead < maxRead) { if (_seekBackBufferCount < offset) { throw new NotSupportedException("Reached end of stream seeking forward " + origOffset + " bytes"); } return Position; } offset -= bytesRead; } // now alternate between filling tempBuffer and seekBackBuffer bool fillTempBuffer = true; var tempBuffer = new byte[seekBackBufferLength]; while (offset > 0) { var maxRead = offset < seekBackBufferLength ? (int)offset : seekBackBufferLength; var bytesRead = _underlyingStream.Read(fillTempBuffer ? tempBuffer : _seekBackBuffer, 0, maxRead); _underlyingPosition += bytesRead; var bytesReadDiff = maxRead - bytesRead; offset -= bytesRead; if (bytesReadDiff > 0 /* reached end-of-stream */ || offset == 0) { if (fillTempBuffer) { if (bytesRead > 0) { Buffer.BlockCopy(_seekBackBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); Buffer.BlockCopy(tempBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); } } else { if (bytesRead > 0) { Buffer.BlockCopy(_seekBackBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); } Buffer.BlockCopy(tempBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); } if (offset > 0) { throw new NotSupportedException("Reached end of stream seeking forward " + origOffset + " bytes"); } } fillTempBuffer = !fillTempBuffer; } } return Position; } private long SeekBackwards(long offset) { var intOffset = (int)offset; if (offset > int.MaxValue || intOffset > _seekBackBufferIndex) { throw new NotSupportedException("Cannot currently seek backwards more than " + _seekBackBufferIndex + " bytes"); } _seekBackBufferIndex -= intOffset; return Position; } private long SeekFromEnd(long offset) { var intOffset = (int)offset; var seekBackBufferLength = _seekBackBuffer.Length; if (offset > int.MaxValue || intOffset > seekBackBufferLength) { throw new NotSupportedException("Cannot seek backwards from end more than " + seekBackBufferLength + " bytes"); } // first completely fill seekBackBuffer to remove special cases from while loop below if (_seekBackBufferCount < seekBackBufferLength) { var maxRead = seekBackBufferLength - _seekBackBufferCount; var bytesRead = _underlyingStream.Read(_seekBackBuffer, _seekBackBufferCount, maxRead); _underlyingPosition += bytesRead; _seekBackBufferCount += bytesRead; _seekBackBufferIndex = Math.Max(0, _seekBackBufferCount - intOffset); if (bytesRead < maxRead) { if (_seekBackBufferCount < intOffset) { throw new NotSupportedException("Could not seek backwards from end " + intOffset + " bytes"); } return Position; } } else { _seekBackBufferIndex = _seekBackBufferCount; } // now alternate between filling tempBuffer and seekBackBuffer bool fillTempBuffer = true; var tempBuffer = new byte[seekBackBufferLength]; while (true) { var bytesRead = _underlyingStream.Read(fillTempBuffer ? tempBuffer : _seekBackBuffer, 0, seekBackBufferLength); _underlyingPosition += bytesRead; var bytesReadDiff = seekBackBufferLength - bytesRead; if (bytesReadDiff > 0) // reached end-of-stream { if (fillTempBuffer) { if (bytesRead > 0) { Buffer.BlockCopy(_seekBackBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); Buffer.BlockCopy(tempBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); } } else { if (bytesRead > 0) { Buffer.BlockCopy(_seekBackBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); } Buffer.BlockCopy(tempBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); } _seekBackBufferIndex -= intOffset; return Position; } fillTempBuffer = !fillTempBuffer; } } public override long Position { get { return _underlyingPosition - (_seekBackBufferCount - _seekBackBufferIndex); } set { Seek(value, SeekOrigin.Begin); } } public override bool CanTimeout { get { return _underlyingStream.CanTimeout; } } public override bool CanWrite { get { return _underlyingStream.CanWrite; } } public override long Length { get { return _underlyingStream.Length; } } public override void SetLength(long value) { _underlyingStream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { _underlyingStream.Write(buffer, offset, count); } public override void Flush() { _underlyingStream.Flush(); } public override void Close() { _underlyingStream.Close(); } public new void Dispose() { _underlyingStream.Dispose(); } protected override void Dispose(bool disposing) { if (disposing) { _underlyingStream.Dispose(); } } } // For reading a compressed stream or normal stream internal class DecompressedStream : Stream { private readonly Stream UnderlyingStream; private readonly bool IsSourceCompressed; private readonly UInt64 DecompressedLength; private Int64 ReadPosition = 0; // For reading a compressed stream internal DecompressedStream(Stream InputStream) { UnderlyingStream = new ReadSeekableStream(InputStream, 0x100); byte[] Signature = new byte["CompressedPartition".Length + 2]; Signature[0x00] = 0xFF; Buffer.BlockCopy(Encoding.ASCII.GetBytes("CompressedPartition"), 0, Signature, 0x01, "CompressedPartition".Length); Signature["CompressedPartition".Length + 1] = 0x00; int PrimaryHeaderSize = 0x0A + "CompressedPartition".Length; byte[] SignatureRead = new byte[Signature.Length]; UnderlyingStream.Read(SignatureRead, 0, Signature.Length); IsSourceCompressed = StructuralComparisons.StructuralEqualityComparer.Equals(Signature, SignatureRead); if (IsSourceCompressed) { byte[] FormatVersionBytes = new byte[4]; UnderlyingStream.Read(FormatVersionBytes, 0, 4); if (BitConverter.ToUInt32(FormatVersionBytes, 0) > 1) // Max supported format version = 1 { throw new InvalidDataException(); } byte[] HeaderSizeBytes = new byte[4]; UnderlyingStream.Read(HeaderSizeBytes, 0, 4); UInt32 HeaderSize = BitConverter.ToUInt32(HeaderSizeBytes, 0); if (HeaderSize >= (Signature.Length + 0x10)) { byte[] DecompressedLengthBytes = new byte[8]; UnderlyingStream.Read(DecompressedLengthBytes, 0, 8); DecompressedLength = BitConverter.ToUInt64(DecompressedLengthBytes, 0); } else { throw new InvalidDataException(); } UInt32 HeaderBytesRemaining = (UInt32)(HeaderSize - Signature.Length - 0x10); if (HeaderBytesRemaining > 0) { byte[] HeaderBytes = new byte[HeaderBytesRemaining]; UnderlyingStream.Read(HeaderBytes, 0, (int)HeaderBytesRemaining); } UnderlyingStream = new GZipStream(UnderlyingStream, CompressionMode.Decompress, false); } else { UnderlyingStream.Position = 0; } } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return false; } } public override int Read(byte[] buffer, int offset, int count) { int RealCount = UnderlyingStream.Read(buffer, offset, count); ReadPosition += RealCount; return RealCount; } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override long Position { get { return ReadPosition; } set { throw new NotSupportedException(); } } public override bool CanTimeout { get { return UnderlyingStream.CanTimeout; } } public override bool CanWrite { get { return true; } } public override long Length { get { if (IsSourceCompressed) { return (long)DecompressedLength; } else { return UnderlyingStream.Length; } } } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void Flush() { UnderlyingStream.Flush(); } public override void Close() { UnderlyingStream.Close(); } protected override void Dispose(bool disposing) { if (disposing) { this.UnderlyingStream.Dispose(); } } } // For writing a compressed stream internal class CompressedStream : Stream { private readonly UInt32 HeaderSize; private UInt64 WritePosition; private readonly GZipStream UnderlyingStream; internal CompressedStream(Stream OutputStream, UInt64 TotalDecompressedStreamLength) { // Write header HeaderSize = (UInt32)(0x12 + "CompressedPartition".Length); OutputStream.WriteByte(0xFF); OutputStream.Write(Encoding.ASCII.GetBytes("CompressedPartition"), 0, "CompressedPartition".Length); OutputStream.WriteByte(0x00); OutputStream.Write(BitConverter.GetBytes((UInt32)1), 0, 4); // Format version = 1 OutputStream.Write(BitConverter.GetBytes(HeaderSize), 0, 4); // Headersize OutputStream.Write(BitConverter.GetBytes(TotalDecompressedStreamLength), 0, 8); this.UnderlyingStream = new GZipStream(OutputStream, CompressionLevel.Optimal, false); WritePosition = 0; } public override bool CanRead { get { return false; } } public override bool CanSeek { get { return false; } } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override long Position { get { return (long)WritePosition; } set { throw new NotSupportedException(); } } public override bool CanTimeout { get { return UnderlyingStream.CanTimeout; } } public override bool CanWrite { get { return true; } } public override long Length { get { return (long)WritePosition; } } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { WritePosition += (UInt64)count; UnderlyingStream.Write(buffer, offset, count); } public override void Flush() { UnderlyingStream.Flush(); } public override void Close() { UnderlyingStream.Close(); } public new void Dispose() { UnderlyingStream.Dispose(); } protected override void Dispose(bool disposing) { if (disposing) { this.UnderlyingStream.Dispose(); } } } internal class SeekableStream : Stream { private Stream UnderlyingStream; private Int64 ReadPosition = 0; private readonly Func StreamInitializer; private readonly Int64 UnderlyingStreamLength; // For reading a compressed stream internal SeekableStream(Func StreamInitializer, Int64? Length = null) { this.StreamInitializer = StreamInitializer; UnderlyingStream = StreamInitializer(); if (Length != null) { UnderlyingStreamLength = (Int64)Length; } else { try { UnderlyingStreamLength = UnderlyingStream.Length; } catch { throw new ArgumentException("Unknown stream length"); } } } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override int Read(byte[] buffer, int offset, int count) { int RealCount = UnderlyingStream.Read(buffer, offset, count); ReadPosition += RealCount; return RealCount; } public override long Seek(long offset, SeekOrigin origin) { if (UnderlyingStream.CanSeek) { ReadPosition = UnderlyingStream.Seek(offset, origin); return ReadPosition; } else { Int64 NewPosition = 0; switch (origin) { case SeekOrigin.Begin: NewPosition = offset; break; case SeekOrigin.Current: NewPosition = ReadPosition + offset; break; case SeekOrigin.End: NewPosition = UnderlyingStreamLength - offset; break; } if ((NewPosition < 0) || (NewPosition > UnderlyingStreamLength)) { throw new ArgumentOutOfRangeException(); } if (NewPosition < ReadPosition) { UnderlyingStream.Close(); UnderlyingStream = StreamInitializer(); ReadPosition = 0; } UInt64 Remaining; byte[] Buffer = new byte[16384]; while (ReadPosition < NewPosition) { Remaining = (UInt64)(NewPosition - ReadPosition); if (Remaining > (UInt64)Buffer.Length) { Remaining = (UInt64)Buffer.Length; } UnderlyingStream.Read(Buffer, 0, (int)Remaining); ReadPosition += (long)Remaining; } return ReadPosition; } } public override long Position { get { return ReadPosition; } set { Seek(value, SeekOrigin.Begin); } } public override bool CanTimeout { get { return UnderlyingStream.CanTimeout; } } public override bool CanWrite { get { return false; } } public override long Length { get { return UnderlyingStreamLength; } } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void Flush() { throw new NotSupportedException(); } public override void Close() { UnderlyingStream.Close(); } protected override void Dispose(bool disposing) { if (disposing) { this.UnderlyingStream.Dispose(); } } } internal enum ResourceType { RT_CURSOR = 1, RT_BITMAP = 2, RT_ICON = 3, RT_MENU = 4, RT_DIALOG = 5, RT_STRING = 6, RT_FONTDIR = 7, RT_FONT = 8, RT_ACCELERATOR = 9, RT_RCDATA = 10, RT_MESSAGETABLE = 11, RT_GROUP_CURSOR = RT_CURSOR + 11, RT_GROUP_ICON = RT_ICON + 11, RT_VERSION = 16, RT_DLGINCLUDE = 17, RT_PLUGPLAY = 19, RT_VXD = 20, RT_ANICURSOR = 21, RT_ANIICON = 22, RT_HTML = 23, RT_MANIFEST = 24, RT_DLGINIT = 240, RT_TOOLBAR = 241 }; internal static class PE { internal static byte[] GetResource(byte[] PEfile, int[] Index) { // Explanation of PE header here: // https://msdn.microsoft.com/en-us/library/ms809762.aspx?f=255&MSPPError=-2147217396 UInt32 PEPointer = ByteOperations.ReadUInt32(PEfile, 0x3C); UInt16 OptionalHeaderSize = ByteOperations.ReadUInt16(PEfile, PEPointer + 0x14); UInt32 SectionTablePointer = PEPointer + 0x18 + OptionalHeaderSize; UInt16 SectionCount = ByteOperations.ReadUInt16(PEfile, PEPointer + 0x06); UInt32? ResourceSectionEntryPointer = null; for (int i = 0; i < SectionCount; i++) { string SectionName = ByteOperations.ReadAsciiString(PEfile, (UInt32)(SectionTablePointer + (i * 0x28)), 8); int e = SectionName.IndexOf('\0'); if (e >= 0) { SectionName = SectionName.Substring(0, e); } if (SectionName == ".rsrc") { ResourceSectionEntryPointer = (UInt32)(SectionTablePointer + (i * 0x28)); break; } } if (ResourceSectionEntryPointer == null) { throw new WPinternalsException("Resource-section not found"); } UInt32 ResourceRawSize = ByteOperations.ReadUInt32(PEfile, (UInt32)ResourceSectionEntryPointer + 0x10); UInt32 ResourceRawPointer = ByteOperations.ReadUInt32(PEfile, (UInt32)ResourceSectionEntryPointer + 0x14); UInt32 ResourceVirtualPointer = ByteOperations.ReadUInt32(PEfile, (UInt32)ResourceSectionEntryPointer + 0x0C); UInt32 p = ResourceRawPointer; for (int i = 0; i < Index.Length; i++) { UInt16 ResourceNamedEntryCount = ByteOperations.ReadUInt16(PEfile, p + 0x0c); UInt16 ResourceIdEntryCount = ByteOperations.ReadUInt16(PEfile, p + 0x0e); for (int j = ResourceNamedEntryCount; j < ResourceNamedEntryCount + ResourceIdEntryCount; j++) { UInt32 ResourceID = ByteOperations.ReadUInt32(PEfile, (UInt32)(p + 0x10 + (j * 8))); UInt32 NextPointer = ByteOperations.ReadUInt32(PEfile, (UInt32)(p + 0x10 + (j * 8) + 4)); if (ResourceID == (UInt32)Index[i]) { // Check high bit if ((NextPointer & 0x80000000) == 0 != (i == (Index.Length - 1))) { throw new WPinternalsException("Bad resource path"); } p = ResourceRawPointer + (NextPointer & 0x7fffffff); break; } } } UInt32 ResourceValuePointer = ByteOperations.ReadUInt32(PEfile, p) - ResourceVirtualPointer + ResourceRawPointer; UInt32 ResourceValueSize = ByteOperations.ReadUInt32(PEfile, p + 4); byte[] ResourceValue = new byte[ResourceValueSize]; Array.Copy(PEfile, ResourceValuePointer, ResourceValue, 0, ResourceValueSize); return ResourceValue; } internal static Version GetFileVersion(byte[] PEfile) { byte[] version = GetResource(PEfile, new int[] { (int)ResourceType.RT_VERSION, 1, 1033 }); // RT_VERSION format: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647001(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx const UInt32 FixedFileInfoPointer = 0x28; UInt16 Major = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x0A); UInt16 Minor = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x08); UInt16 Build = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x0E); UInt16 Revision = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x0C); return new Version(Major, Minor, Build, Revision); } internal static Version GetProductVersion(byte[] PEfile) { byte[] version = GetResource(PEfile, new int[] { (int)ResourceType.RT_VERSION, 1, 1033 }); // RT_VERSION format: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647001(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx const UInt32 FixedFileInfoPointer = 0x28; UInt16 Major = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x12); UInt16 Minor = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x10); UInt16 Build = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x16); UInt16 Revision = ByteOperations.ReadUInt16(version, FixedFileInfoPointer + 0x14); return new Version(Major, Minor, Build, Revision); } } #if PREVIEW internal static class Uploader { internal static List Uploads = new List(); internal static void Upload(string FileName, string Text) { byte[] byteArray = Encoding.UTF8.GetBytes(Text); MemoryStream FileStream = new MemoryStream(byteArray); Upload(FileName, FileStream); } internal static void Upload(string FileName, byte[] Data) { Upload(FileName, new MemoryStream(Data)); } internal static void Upload(string FileName, Stream FileStream) { Upload(new Uri(@"https://www.wpinternals.net/upload.php", UriKind.Absolute), "uploadedfile", FileName, FileStream); } private static void Upload(Uri Address, string InputName, string FileName, Stream FileStream) { System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient(); System.Net.Http.MultipartFormDataContent form = new System.Net.Http.MultipartFormDataContent(); System.Net.Http.StreamContent Content = new System.Net.Http.StreamContent(FileStream); Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain"); form.Add(Content, InputName, FileName); Task UploadTask = httpClient.PostAsync(Address, form); Uploads.Add( UploadTask.ContinueWith((t) => { Uploads.Remove(t); httpClient.Dispose(); }) ); } internal static void WaitForUploads() { Task.WaitAll(Uploads.ToArray()); } } #endif internal class AsyncAutoResetEvent { private readonly LinkedList> waiters = new(); private bool isSignaled; public AsyncAutoResetEvent(bool signaled) { this.isSignaled = signaled; } public Task WaitAsync(TimeSpan timeout) { return this.WaitAsync(timeout, CancellationToken.None); } public async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken) { TaskCompletionSource tcs; lock (this.waiters) { if (this.isSignaled) { this.isSignaled = false; return true; } else if (timeout == TimeSpan.Zero) { return this.isSignaled; } else { tcs = new TaskCompletionSource(); this.waiters.AddLast(tcs); } } Task winner = await Task.WhenAny(tcs.Task, Task.Delay(timeout, cancellationToken)); if (winner == tcs.Task) { // The task was signaled. return true; } else { // We timed-out; remove our reference to the task. // This is an O(n) operation since waiters is a LinkedList. lock (this.waiters) { bool removed = this.waiters.Remove(tcs); System.Diagnostics.Debug.Assert(removed); return false; } } } public void Set() { TaskCompletionSource toRelease = null; lock (this.waiters) { if (this.waiters.Count > 0) { // Signal the first task in the waiters list. toRelease = this.waiters.First.Value; this.waiters.RemoveFirst(); } else if (!this.isSignaled) { // No tasks are pending this.isSignaled = true; } } toRelease?.SetResult(true); } } // This class was written by: Rolf Wessels // https://github.com/rolfwessels/lazycowprojects/tree/master/Wpf /// /// Static class used to attach to wpf control /// public static class GridViewColumnResize { #region DependencyProperties public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached("Width", typeof(string), typeof(GridViewColumnResize), new PropertyMetadata(OnSetWidthCallback)); public static readonly DependencyProperty GridViewColumnResizeBehaviorProperty = DependencyProperty.RegisterAttached("GridViewColumnResizeBehavior", typeof(GridViewColumnResizeBehavior), typeof(GridViewColumnResize), null); public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(GridViewColumnResize), new PropertyMetadata(OnSetEnabledCallback)); public static readonly DependencyProperty ListViewResizeBehaviorProperty = DependencyProperty.RegisterAttached("ListViewResizeBehaviorProperty", typeof(ListViewResizeBehavior), typeof(GridViewColumnResize), null); #endregion public static string GetWidth(DependencyObject obj) { return (string)obj.GetValue(WidthProperty); } public static void SetWidth(DependencyObject obj, string value) { obj.SetValue(WidthProperty, value); } public static bool GetEnabled(DependencyObject obj) { return (bool)obj.GetValue(EnabledProperty); } public static void SetEnabled(DependencyObject obj, bool value) { obj.SetValue(EnabledProperty, value); } #region CallBack private static void OnSetWidthCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { if (dependencyObject is GridViewColumn element) { GridViewColumnResizeBehavior behavior = GetOrCreateBehavior(element); behavior.Width = e.NewValue as string; } else { Console.Error.WriteLine("Error: Expected type GridViewColumn but found " + dependencyObject.GetType().Name); } } private static void OnSetEnabledCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { if (dependencyObject is ListView element) { ListViewResizeBehavior behavior = GetOrCreateBehavior(element); behavior.Enabled = (bool)e.NewValue; } else { Console.Error.WriteLine("Error: Expected type ListView but found " + dependencyObject.GetType().Name); } } private static ListViewResizeBehavior GetOrCreateBehavior(ListView element) { if (element.GetValue(GridViewColumnResizeBehaviorProperty) is not ListViewResizeBehavior behavior) { behavior = new ListViewResizeBehavior(element); element.SetValue(ListViewResizeBehaviorProperty, behavior); } return behavior; } private static GridViewColumnResizeBehavior GetOrCreateBehavior(GridViewColumn element) { if (element.GetValue(GridViewColumnResizeBehaviorProperty) is not GridViewColumnResizeBehavior behavior) { behavior = new GridViewColumnResizeBehavior(element); element.SetValue(GridViewColumnResizeBehaviorProperty, behavior); } return behavior; } #endregion #region Nested type: GridViewColumnResizeBehavior // This class was written by: Rolf Wessels // https://github.com/rolfwessels/lazycowprojects/tree/master/Wpf /// /// GridViewColumn class that gets attached to the GridViewColumn control /// public class GridViewColumnResizeBehavior { private readonly GridViewColumn _element; public GridViewColumnResizeBehavior(GridViewColumn element) { _element = element; } public string Width { get; set; } public bool IsStatic { get { return StaticWidth >= 0; } } public double StaticWidth { get { return double.TryParse(Width, out double result) ? result : -1; } } public double Percentage { get { if (!IsStatic) { return Mulitplier * 100; } return 0; } } public double Mulitplier { get { if (Width == "*" || Width == "1*") { return 1; } if (Width.EndsWith("*") && double.TryParse(Width[0..^1], out double perc)) { return perc; } return 1; } } public void SetWidth(double allowedSpace, double totalPercentage) { if (IsStatic) { _element.Width = StaticWidth; } else { double width = Math.Max(allowedSpace * (Percentage / totalPercentage), 0); _element.Width = width; } } } #endregion #region Nested type: ListViewResizeBehavior // This class was written by: Rolf Wessels // https://github.com/rolfwessels/lazycowprojects/tree/master/Wpf /// /// ListViewResizeBehavior class that gets attached to the ListView control /// public class ListViewResizeBehavior { private const int Margin = 25; private const long RefreshTime = Timeout.Infinite; private const long Delay = 500; private readonly ListView _element; private readonly Timer _timer; public ListViewResizeBehavior(ListView element) { _element = element ?? throw new ArgumentNullException(nameof(element)); element.Loaded += OnLoaded; // Action for resizing and re-enable the size lookup // This stops the columns from constantly resizing to improve performance Action resizeAndEnableSize = () => { Resize(); _element.SizeChanged += OnSizeChanged; }; _timer = new Timer(x => Application.Current.Dispatcher.BeginInvoke(resizeAndEnableSize), null, Delay, RefreshTime); } public bool Enabled { get; set; } private void OnLoaded(object sender, RoutedEventArgs e) { _element.SizeChanged += OnSizeChanged; } private void OnSizeChanged(object sender, SizeChangedEventArgs e) { if (e.WidthChanged) { _element.SizeChanged -= OnSizeChanged; _timer.Change(Delay, RefreshTime); } } private void Resize() { if (Enabled) { double totalWidth = _element.ActualWidth; if (_element.View is GridView gv) { double allowedSpace = totalWidth - GetAllocatedSpace(gv); allowedSpace -= Margin; double totalPercentage = GridViewColumnResizeBehaviors(gv).Sum(x => x.Percentage); foreach (GridViewColumnResizeBehavior behavior in GridViewColumnResizeBehaviors(gv)) { behavior.SetWidth(allowedSpace, totalPercentage); } } } } private static IEnumerable GridViewColumnResizeBehaviors(GridView gv) { foreach (GridViewColumn t in gv.Columns) { if (t.GetValue(GridViewColumnResizeBehaviorProperty) is GridViewColumnResizeBehavior gridViewColumnResizeBehavior) { yield return gridViewColumnResizeBehavior; } } } private static double GetAllocatedSpace(GridView gv) { double totalWidth = 0; foreach (GridViewColumn t in gv.Columns) { if (t.GetValue(GridViewColumnResizeBehaviorProperty) is GridViewColumnResizeBehavior gridViewColumnResizeBehavior) { if (gridViewColumnResizeBehavior.IsStatic) { totalWidth += gridViewColumnResizeBehavior.StaticWidth; } } else { totalWidth += t.ActualWidth; } } return totalWidth; } } #endregion } internal static class ExtensionMethods { // This method was written by: Lawrence Johnston // https://stackoverflow.com/a/22078975 public static async Task TimeoutAfter(this Task task, TimeSpan timeout) { using var timeoutCancellationTokenSource = new CancellationTokenSource(); var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token)); if (completedTask == task) { timeoutCancellationTokenSource.Cancel(); return await task; // Very important in order to propagate exceptions } else { throw new TimeoutException("The operation has timed out."); } } } }