diff --git a/PortableTerrariaCommon/PortableTerrariaCommon.sln b/PortableTerrariaCommon/PortableTerrariaCommon.sln new file mode 100644 index 0000000..f6cb3aa --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32228.343 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PortableTerrariaCommon", "PortableTerrariaCommon\PortableTerrariaCommon.csproj", "{AFB76C63-9158-4CE9-8CFB-21DF06EE8FB9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AFB76C63-9158-4CE9-8CFB-21DF06EE8FB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFB76C63-9158-4CE9-8CFB-21DF06EE8FB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFB76C63-9158-4CE9-8CFB-21DF06EE8FB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFB76C63-9158-4CE9-8CFB-21DF06EE8FB9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {80AFC25F-AA34-428B-99E1-4C59C70D4FA5} + EndGlobalSection +EndGlobal diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/App.config b/PortableTerrariaCommon/PortableTerrariaCommon/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/AtomicObj.cs b/PortableTerrariaCommon/PortableTerrariaCommon/AtomicObj.cs new file mode 100644 index 0000000..32944b9 --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon/AtomicObj.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sahlaysta.PortableTerrariaCommon +{ + //thread safe atomic obj + class AtomicObj + { + //constructor + public AtomicObj(T obj = default) + { + val = obj; + } + + //public operations + public T Value + { + get + { + lock (objLock) + { + return (T)val; + } + } + set + { + lock (objLock) + { + val = value; + } + } + } + + volatile object val; + readonly object objLock = new object(); + } +} diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/DotNetZip.dll b/PortableTerrariaCommon/PortableTerrariaCommon/DotNetZip.dll new file mode 100644 index 0000000..e4dd8a2 Binary files /dev/null and b/PortableTerrariaCommon/PortableTerrariaCommon/DotNetZip.dll differ diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/DotNetZipAssembly.cs b/PortableTerrariaCommon/PortableTerrariaCommon/DotNetZipAssembly.cs new file mode 100644 index 0000000..4cb9f76 --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon/DotNetZipAssembly.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Sahlaysta.PortableTerrariaCommon +{ + // DotNetZip assembly reflection class + static class DotNetZipAssembly + { + //reflection ZipFile + public class ZipFile : IEnumerable, IDisposable + { + //reflection enumerator + class ZipEntryEnumerator : IEnumerator + { + //reflection constructor + public ZipEntryEnumerator(IEnumerator enumerator) + { + ie = enumerator; + } + + //interface operations + ZipEntry IEnumerator.Current => getCurrent(); + object IEnumerator.Current => getCurrent(); + void IDisposable.Dispose() => ((IDisposable)ie).Dispose(); + bool IEnumerator.MoveNext() + { + bool result = ie.MoveNext(); + if (result) + current = null; + return result; + } + void IEnumerator.Reset() + { + ie.Reset(); + current = null; + } + + ZipEntry getCurrent() + { + return current ?? (current = new ZipEntry(ie.Current)); + } + + readonly IEnumerator ie; + ZipEntry current; + } + + //reflection constructor + public ZipFile() : this( + (IDisposable)rZipFileConstr.Invoke(null)) { } + ZipFile(IDisposable instance) + { + this.instance = instance; + addSaveProgressEventHandler(invokeSaveProgress); + } + public static ZipFile Read(Stream arg0) + { + return new ZipFile( + (IDisposable)rRead.Invoke(null, new object[] { arg0 })); + } + + //reflection event + public event EventHandler SaveProgress; + + //public reflection operations + public int Count + { + get => (int)rCount.GetValue(instance); + } + public void AddFile(string arg0) + { + rAddFile.Invoke(instance, new object[] { arg0 }); + } + public void AddFile(string arg0, string arg1) + { + rAddFile2.Invoke(instance, new object[] { arg0, arg1 }); + } + public void AddDirectory(string arg0) + { + rAddDirectory.Invoke(instance, new object[] { arg0 }); + } + public void AddDirectory(string arg0, string arg1) + { + rAddDirectory2.Invoke(instance, new object[] { arg0, arg1 }); + } + public void Save(Stream arg0) + { + rSave.Invoke(instance, new object[] { arg0 }); + } + public IEnumerator GetEnumerator() + { + return getEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() + { + return getEnumerator(); + } + public void Dispose() + { + instance.Dispose(); + } + + //reflection SaveProgress event + void addSaveProgressEventHandler(EventHandler eh) + { + var d = (Delegate) + rSaveProgressConstr.Invoke(new object[] + { + eh.Target, + eh.Method.MethodHandle.GetFunctionPointer() + }); + rSaveProgress.AddEventHandler(instance, d); + } + void invokeSaveProgress(object sender, EventArgs e) + { + var eh = SaveProgress; + if (eh == null || eh.GetInvocationList().Length == 0) + return; + + eh.Invoke(sender, new SaveProgressEventArgs(e)); + } + + IEnumerator getEnumerator() + { + IEnumerator ie = (IEnumerator) + rEnumerator.Invoke(instance, null); + return new ZipEntryEnumerator(ie); + } + + readonly IDisposable instance; + } + + //reflection ZipEntry + public class ZipEntry + { + //reflection constructor + internal ZipEntry(object instance) + { + this.instance = instance; + } + + //public reflection operations + public string FileName + { + get => (string)rFileName.GetValue(instance); + } + public void Extract(Stream arg0) + { + rExtract.Invoke(instance, new object[] { arg0 }); + } + + readonly object instance; + } + + //reflection SaveProgressEventArgs + public class SaveProgressEventArgs : EventArgs + { + //reflection constructor + internal SaveProgressEventArgs(EventArgs e) + { + this.e = e; + } + + //public reflection operations + public bool Cancel + { + get => (bool)rCancel.GetValue(e); + set => rCancel.SetValue(e, value); + } + public bool EventTypeIsSaving_AfterWriteEntry + { + get => rEventType.GetValue(e) + .Equals(rSaving_AfterWriteEntry); + } + public int EntriesSaved + { + get => (int)rEntriesSaved.GetValue(e); + } + public int EntriesTotal + { + get => (int)rEntriesTotal.GetValue(e); + } + + readonly EventArgs e; + } + + //assembly + static readonly Assembly assembly = + Assembly.Load(GuiHelper.GetResourceBytes( + "DotNetZip.dll")); + + //reflection fields + static readonly Type rZipFile = + assembly.GetType( + "Ionic.Zip.ZipFile"); + static readonly ConstructorInfo rZipFileConstr = + rZipFile.GetConstructors() + .First(c => c.GetParameters().Length == 0); + static readonly PropertyInfo rCount = + rZipFile.GetProperty( + "Count"); + static readonly MethodInfo rRead = + rZipFile.GetMethod( + "Read", + new Type[] { typeof(Stream) }); + static readonly MethodInfo rAddFile = + rZipFile.GetMethod( + "AddFile", + new Type[] { typeof(string) }); + static readonly MethodInfo rAddFile2 = + rZipFile.GetMethod( + "AddFile", + new Type[] { typeof(string), typeof(string) }); + static readonly MethodInfo rAddDirectory = + rZipFile.GetMethod( + "AddDirectory", + new Type[] { typeof(string) }); + static readonly MethodInfo rAddDirectory2 = + rZipFile.GetMethod( + "AddDirectory", + new Type[] { typeof(string), typeof(string) }); + static readonly MethodInfo rSave = + rZipFile.GetMethod( + "Save", + new Type[] { typeof(Stream) }); + static readonly EventInfo rSaveProgress = + rZipFile.GetEvent( + "SaveProgress"); + static readonly ConstructorInfo rSaveProgressConstr = + rSaveProgress.EventHandlerType.GetConstructor( + new Type[] { typeof(object), typeof(IntPtr) }); + static readonly Type rEnumType = + assembly.GetType("Ionic.Zip.ZipProgressEventType"); + static readonly Type rEventArgsType = + assembly.GetType("Ionic.Zip.SaveProgressEventArgs"); + static readonly object rSaving_AfterWriteEntry = + Enum.GetValues(rEnumType).Cast() + .First(v => v.ToString() == "Saving_AfterWriteEntry"); + static readonly PropertyInfo rCancel = + rEventArgsType.GetProperty("Cancel"); + static readonly PropertyInfo rEventType = + rEventArgsType.GetProperty("EventType"); + static readonly PropertyInfo rEntriesSaved = + rEventArgsType.GetProperty("EntriesSaved"); + static readonly PropertyInfo rEntriesTotal = + rEventArgsType.GetProperty("EntriesTotal"); + static readonly Type rZipEntry = + assembly.GetType("Ionic.Zip.ZipEntry"); + static readonly MethodInfo rEnumerator = + rZipFile.GetMethod( + "GetEnumerator"); + static readonly PropertyInfo rFileName = + rZipEntry.GetProperty( + "FileName"); + static readonly MethodInfo rExtract = + rZipEntry.GetMethod( + "Extract", + new Type[] { typeof(Stream) }); + } +} diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/FolderBrowser2.cs b/PortableTerrariaCommon/PortableTerrariaCommon/FolderBrowser2.cs new file mode 100644 index 0000000..8b327e0 --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon/FolderBrowser2.cs @@ -0,0 +1,185 @@ +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace Sahlaysta.PortableTerrariaCommon +{ + //uses the good folder-browser dialog and not the ugly one + + //this class is written by Simon Mourier, published as + //a Stack Overflow answer, under the MIT license: + //https://stackoverflow.com/a/15386992/ + //information on Stack Overflow licensing: + //https://meta.stackexchange.com/questions/271080 + + + /// + /// Displays a Vista-style directory browse dialog (IFileOpenDialog). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] + class FolderBrowser2 : IDisposable + { + /// + /// The default and user-selected directory path. + /// + public string DirectoryPath { get; set; } + public IntPtr? HWndOwner { get; set; } + public string Title { get; set; } + + /// + /// Shows the dialog. + /// + /// DialogResult. + public DialogResult ShowDialog() + { + IntPtr hwndOwner = HWndOwner ?? FindWindow("MsiDialogCloseClass", 0); + + IFileOpenDialog dialog = (IFileOpenDialog)new FileOpenDialog(); + string title = Title; + if (title != null) + dialog.SetTitle(title); + try + { + IShellItem item; + if (!string.IsNullOrEmpty(DirectoryPath)) + { + IntPtr idl; + uint atts = 0; + if (SHILCreateFromPath(DirectoryPath, out idl, ref atts) == 0) + { + if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item) == 0) + { + dialog.SetFolder(item); + } + } + } + dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_FORCEFILESYSTEM); + uint hr = dialog.Show(hwndOwner); + if (hr == ERROR_CANCELLED) + return DialogResult.Cancel; + + if (hr != 0) + return DialogResult.Abort; + + dialog.GetResult(out item); + string path; + item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out path); + DirectoryPath = path; + return DialogResult.OK; + } + finally + { + Marshal.ReleaseComObject(dialog); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] + public void Dispose() + { + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("shell32.dll")] + private static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("shell32.dll")] + private static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IShellItem ppsi); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "1"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr FindWindow(string strClassName, int nptWindowName); + + private const uint ERROR_CANCELLED = 0x800704C7; + + [ComImport] + [Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] + private class FileOpenDialog + { + } + + [ComImport] + [Guid("42f85136-db7e-439c-85f1-e4075d135fc8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IFileOpenDialog + { + [PreserveSig] + uint Show([In] IntPtr parent); // IModalWindow + void SetFileTypes(); // not fully defined + void SetFileTypeIndex([In] uint iFileType); + void GetFileTypeIndex(out uint piFileType); + void Advise(); // not fully defined + void Unadvise(); + void SetOptions([In] FOS fos); + void GetOptions(out FOS pfos); + void SetDefaultFolder(IShellItem psi); + void SetFolder(IShellItem psi); + void GetFolder(out IShellItem ppsi); + void GetCurrentSelection(out IShellItem ppsi); + void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); + void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); + void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); + void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + void GetResult(out IShellItem ppsi); + void AddPlace(IShellItem psi, int alignment); + void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + void Close(int hr); + void SetClientGuid(); // not fully defined + void ClearClientData(); + void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); + void GetResults([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenum); // not fully defined + void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppsai); // not fully defined + } + + [ComImport] + [Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IShellItem + { + void BindToHandler(); // not fully defined + void GetParent(); // not fully defined + void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); + void GetAttributes(); // not fully defined + void Compare(); // not fully defined + } + + private enum SIGDN : uint + { + SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000, + SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000, + SIGDN_FILESYSPATH = 0x80058000, + SIGDN_NORMALDISPLAY = 0, + SIGDN_PARENTRELATIVE = 0x80080001, + SIGDN_PARENTRELATIVEEDITING = 0x80031001, + SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001, + SIGDN_PARENTRELATIVEPARSING = 0x80018001, + SIGDN_URL = 0x80068000 + } + + [Flags] + private enum FOS + { + FOS_ALLNONSTORAGEITEMS = 0x80, + FOS_ALLOWMULTISELECT = 0x200, + FOS_CREATEPROMPT = 0x2000, + FOS_DEFAULTNOMINIMODE = 0x20000000, + FOS_DONTADDTORECENT = 0x2000000, + FOS_FILEMUSTEXIST = 0x1000, + FOS_FORCEFILESYSTEM = 0x40, + FOS_FORCESHOWHIDDEN = 0x10000000, + FOS_HIDEMRUPLACES = 0x20000, + FOS_HIDEPINNEDPLACES = 0x40000, + FOS_NOCHANGEDIR = 8, + FOS_NODEREFERENCELINKS = 0x100000, + FOS_NOREADONLYRETURN = 0x8000, + FOS_NOTESTFILECREATE = 0x10000, + FOS_NOVALIDATE = 0x100, + FOS_OVERWRITEPROMPT = 2, + FOS_PATHMUSTEXIST = 0x800, + FOS_PICKFOLDERS = 0x20, + FOS_SHAREAWARE = 0x4000, + FOS_STRICTFILETYPES = 4 + } + } +} diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/GuiDisableableControl.cs b/PortableTerrariaCommon/PortableTerrariaCommon/GuiDisableableControl.cs new file mode 100644 index 0000000..8a2d943 --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon/GuiDisableableControl.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Sahlaysta.PortableTerrariaCommon +{ + //shows radiobuttons "Enable" and "Disable" grouped on top of a control + class GuiDisableableControl : Panel + { + //constructor + public GuiDisableableControl() + { + initRadioButtons(); + + adjustBounds(); + ControlAdded += (o, e) => adjustBounds(true); + ControlRemoved += (o, e) => adjustBounds(); + Resize += (o, e) => adjustBounds(); + } + + //public operations + public RadioButton EnableRadioButton { get { return enableBtn; } } + public RadioButton DisableRadioButton { get { return disableBtn; } } + public override Size GetPreferredSize(Size proposedSize) + { + int width = base.GetPreferredSize(proposedSize).Width; + int height = y; + return new Size(width, height); + } + + //initializations + RadioButton enableBtn, disableBtn; + void initRadioButtons() + { + enableBtn = new RadioButton() + { + AutoSize = true, + Text = "Enable" + }; + disableBtn = new RadioButton() + { + AutoSize = true, + Text = "Disable" + }; + enableBtn.Click += (o, e) => + { + Control control = getDC(); + if (control != null) + control.Enabled = true; + }; + disableBtn.Click += (o, e) => + { + Control control = getDC(); + if (control != null) + control.Enabled = false; + }; + Controls.Add(enableBtn); + Controls.Add(disableBtn); + updateRadioButtons(); + enableBtn.TextChanged += (o, e) => updateRadioButtons(); + disableBtn.TextChanged += (o, e) => updateRadioButtons(); + } + + //adjust location + size + int y; + void adjustBounds(bool adding = false) + { + Control control = getDC(); + + y = Math.Max(enableBtn.Bounds.Height, disableBtn.Bounds.Height); + + if (control != null) + { + //control positioning + var cs = ClientSize; + control.Location = new Point(0, y); + control.Size = new Size(cs.Width, cs.Height - y); + + //set bounds + y += control.PreferredSize.Height; + + //radiobutton selection + if (adding) + disableBtn.Checked = !(enableBtn.Checked = control.Enabled); + } + } + + //placement of the two radiobuttons based on their text width + void updateRadioButtons() + { + enableBtn.Location = new Point(0, 0); + disableBtn.Left = 30 + TextRenderer.MeasureText( + enableBtn.Text, enableBtn.Font).Width; + } + + Control getDC() => Controls.Cast().FirstOrDefault( + c => c != enableBtn && c != disableBtn); + } +} diff --git a/PortableTerrariaCommon/PortableTerrariaCommon/GuiDllGridView.cs b/PortableTerrariaCommon/PortableTerrariaCommon/GuiDllGridView.cs new file mode 100644 index 0000000..082710f --- /dev/null +++ b/PortableTerrariaCommon/PortableTerrariaCommon/GuiDllGridView.cs @@ -0,0 +1,448 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Sahlaysta.PortableTerrariaCommon +{ + class GuiDllGridView : UserControl + { + //constructor + public GuiDllGridView() + { + init(); + } + + //public operations + public IEnumerable Dlls + { + get => dlls.Length == 0 ? null : dlls; + } + + //initializations + void init() + { + initButtons(); + initDataGridView(); + adjustBounds(); + Resize += (o, e) => adjustBounds(); + } + + //init buttons + List