From b4329cb1571abb0f6ec7a57cf8a0f088d5684638 Mon Sep 17 00:00:00 2001
From: Read <decimation001@gmail.com>
Date: Fri, 9 Dec 2022 14:27:29 -0600
Subject: [PATCH] Additional clipboard functionality; work on #32

---
 SmartImage 3/App/Integration.cs               | 29 ++++++++
 SmartImage 3/GuiMain.cs                       |  6 +-
 SmartImage 3/ImageUtility.cs                  | 68 +++++++++++++++++++
 SmartImage.Lib 3/Engines/BaseSearchEngine.cs  |  4 +-
 .../Engines/Search/Ascii2DEngine.cs           | 15 +++-
 .../Engines/Search/EHentaiEngine.cs           | 24 +++----
 6 files changed, 127 insertions(+), 19 deletions(-)

diff --git a/SmartImage 3/App/Integration.cs b/SmartImage 3/App/Integration.cs
index 301a9ea9..b73f6ef9 100644
--- a/SmartImage 3/App/Integration.cs	
+++ b/SmartImage 3/App/Integration.cs	
@@ -8,6 +8,7 @@
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics;
+using System.Drawing;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.InteropServices;
@@ -228,6 +229,23 @@ public static void KeepOnTop(bool add)
 		IsOnTop = add;
 	}
 
+	public static bool ReadClipboardImage(out byte[] i)
+	{
+		const uint png = (uint)ClipboardFormat.PNG;
+
+		if (Native.IsClipboardFormatAvailable(png))
+		{
+			var data    = Native.GetClipboardData(png);
+			var pngData = ImageUtility.ReadPNG(data);
+			i = pngData;
+			return true;
+		}
+		else {
+			i = null;
+			return false;
+		}
+	}
+
 	public static bool ReadClipboard(out string str)
 	{
 		Native.OpenClipboard();
@@ -238,6 +256,17 @@ public static bool ReadClipboard(out string str)
 			str = (string) Native.GetClipboard((uint) ClipboardFormat.CF_TEXT);
 		}
 
+		if (ReadClipboardImage(out var ms)) {
+			var s = Path.Combine(Path.GetTempPath(), $"clipboard_{ms.Length}.png");
+			if (!File.Exists(s)) {
+				File.WriteAllBytes(s, ms);
+
+			}
+
+			str = s;
+			Debug.WriteLine($"read png from clipboard {s}");
+		}
+
 		Native.CloseClipboard();
 		// Debug.WriteLine($"Clipboard data: {str}");
 
diff --git a/SmartImage 3/GuiMain.cs b/SmartImage 3/GuiMain.cs
index 0c1aea38..f448ef2e 100644
--- a/SmartImage 3/GuiMain.cs	
+++ b/SmartImage 3/GuiMain.cs	
@@ -610,8 +610,10 @@ private bool ClipboardCallback(MainLoop c)
 			 *	- Input is already ready
 			 *	- Clipboard history contains it already
 			 */
-			if (Integration.ReadClipboard(out var str) &&
-			    !SearchQuery.IsUriOrFile(Tf_Input.Text.ToString()) && !m_clipboard.Contains(str)) {
+			if (!SearchQuery.IsUriOrFile(Tf_Input.Text.ToString()) 
+			    && Integration.ReadClipboard(out var str) 
+			    && !m_clipboard.Contains(str)) {
+				
 				SetInputText(str);
 				// Lbl_InputOk.Text   = UI.Clp;
 				Lbl_InputInfo.Text = R2.Inf_Clipboard;
diff --git a/SmartImage 3/ImageUtility.cs b/SmartImage 3/ImageUtility.cs
index cd2d08f1..24abb96a 100644
--- a/SmartImage 3/ImageUtility.cs	
+++ b/SmartImage 3/ImageUtility.cs	
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
 using Flurl.Http;
@@ -119,4 +120,71 @@ public double score
 		return default;
 	}*/
 
+	/// <summary>
+	/// Reads a <c>PNG</c>
+	/// </summary>
+	/// <remarks>Specifically designed for Windows Snipping Tool <c>PNG</c> format</remarks>
+	public static byte[] ReadPNG(nint data)
+	{
+		/*
+		 * TODO: optimize
+		 */
+
+		int  i = 0;
+		byte b;
+		var  rg = new List<byte>();
+
+		// IEND
+		ReadOnlySpan<byte> s = stackalloc byte[]
+		{
+			0x49,
+			0x45,
+			0x4E,
+			0x44
+		};
+
+		void Read()
+		{
+			try {
+				b = Marshal.ReadByte(data, i++);
+				rg.Add(b);
+			}
+			catch (AccessViolationException x) {
+				b = Byte.MaxValue;
+			}
+			finally { }
+		}
+
+		do {
+			Read();
+
+			if (b == s[0]) {
+				Read();
+
+				if (b == s[1]) {
+					Read();
+
+					if (b == s[2]) {
+						Read();
+
+						if (b == s[3]) {
+							var rg2 = new byte[]
+							{
+								Marshal.ReadByte(data, i++),
+								Marshal.ReadByte(data, i++),
+								Marshal.ReadByte(data, i++),
+								Marshal.ReadByte(data, i++),
+
+							};
+							rg.AddRange(rg2);
+							break;
+						}
+					}
+				}
+			}
+
+		} while (true);
+
+		return rg.ToArray();
+	}
 }
\ No newline at end of file
diff --git a/SmartImage.Lib 3/Engines/BaseSearchEngine.cs b/SmartImage.Lib 3/Engines/BaseSearchEngine.cs
index fb03b4e4..b9211193 100644
--- a/SmartImage.Lib 3/Engines/BaseSearchEngine.cs	
+++ b/SmartImage.Lib 3/Engines/BaseSearchEngine.cs	
@@ -39,13 +39,13 @@ protected BaseSearchEngine(string baseUrl)
 
 	static BaseSearchEngine()
 	{
-		FlurlHttp.Configure(settings =>
+		/*FlurlHttp.Configure(settings =>
 		{
 			settings.Redirects.Enabled                    = true; // default true
 			settings.Redirects.AllowSecureToInsecure      = true; // default false
 			settings.Redirects.ForwardAuthorizationHeader = true; // default false
 			settings.Redirects.MaxAutoRedirects           = 15;   // default 10 (consecutive)
-		});
+		});*/
 
 		// Trace.WriteLine($"Configured HTTP", nameof(BaseSearchEngine));
 	}
diff --git a/SmartImage.Lib 3/Engines/Search/Ascii2DEngine.cs b/SmartImage.Lib 3/Engines/Search/Ascii2DEngine.cs
index 63c939d8..90beed08 100644
--- a/SmartImage.Lib 3/Engines/Search/Ascii2DEngine.cs	
+++ b/SmartImage.Lib 3/Engines/Search/Ascii2DEngine.cs	
@@ -6,6 +6,8 @@
 using AngleSharp.Html.Parser;
 using AngleSharp.XPath;
 using Flurl.Http;
+using Jint;
+using Jint.Parser;
 using Kantan.Net.Utilities;
 
 // ReSharper disable CognitiveComplexity
@@ -19,7 +21,7 @@ namespace SmartImage.Lib.Engines.Search;
 
 public sealed class Ascii2DEngine : BaseSearchEngine, IWebContentEngine
 {
-	public Ascii2DEngine() : base("https://ascii2d.net/search/uri/")
+	public Ascii2DEngine() : base("https://ascii2d.net/search/url/")
 	{
 		Timeout = TimeSpan.FromSeconds(6);
 		MaxSize = 5 * 1000 * 1000;
@@ -72,6 +74,14 @@ public async Task<IDocument> GetDocumentAsync(object origin2, SearchQuery query,
 					{ new StringContent(origin),"uri" }
 				};
 
+				FlurlHttp.Configure(settings =>
+				{
+					settings.Redirects.Enabled                    = true; // default true
+					settings.Redirects.AllowSecureToInsecure      = true; // default false
+					settings.Redirects.ForwardAuthorizationHeader = true; // default false
+					settings.Redirects.MaxAutoRedirects           = 20;   // default 10 (consecutive)
+				});
+
 				var res = await origin.AllowAnyHttpStatus()
 				                      .WithCookies(out var cj)
 				                      .WithTimeout(timeout.Value)
@@ -84,8 +94,7 @@ public async Task<IDocument> GetDocumentAsync(object origin2, SearchQuery query,
 									  {
 										  s.ExceptionHandled = true;
 									  })*/
-				                      .PostAsync(data);
-
+				                      .GetAsync();
 				var str = await res.GetStringAsync();
 
 				var document = await parser.ParseDocumentAsync(str, token.Value);
diff --git a/SmartImage.Lib 3/Engines/Search/EHentaiEngine.cs b/SmartImage.Lib 3/Engines/Search/EHentaiEngine.cs
index 5a93cd52..6d9788ac 100644
--- a/SmartImage.Lib 3/Engines/Search/EHentaiEngine.cs	
+++ b/SmartImage.Lib 3/Engines/Search/EHentaiEngine.cs	
@@ -38,12 +38,12 @@ public EHentaiEngine() : base(EXHENTAI_URI)
 
 	public override SearchEngineOptions EngineOption => SearchEngineOptions.EHentai;
 
-	public string Username      { get; set; }
-	public string Password      { get; set; }
+	public string Username { get; set; }
+	public string Password { get; set; }
 
 	private bool m_isLoggedIn;
 
-	public  bool IsLoggedIn => m_isLoggedIn ;
+	public bool IsLoggedIn => m_isLoggedIn;
 
 	public string NodesSelector => "//table/tbody/tr";
 
@@ -134,15 +134,15 @@ public async Task<IDocument> GetDocumentAsync(object origin, SearchQuery query,
 		return await parser.ParseDocumentAsync(content);
 	}
 
-	private async Task GetSessionAsync()
+	private async Task<IFlurlResponse> GetSessionAsync()
 	{
-		var res = await EXHENTAI_URI.WithCookies(Cookies)
-		                            .WithHeaders(new
-		                            {
-			                            User_Agent = HttpUtilities.UserAgent
-		                            })
-		                            .WithAutoRedirect(true)
-		                            .GetAsync();
+		return await EXHENTAI_URI.WithCookies(Cookies)
+		                           .WithHeaders(new
+		                           {
+			                           User_Agent = HttpUtilities.UserAgent
+		                           })
+		                           .WithAutoRedirect(true)
+		                           .GetAsync();
 	}
 
 	/*
@@ -177,7 +177,7 @@ public async Task LoginAsync()
 			Cookies.AddOrReplace(fc);
 		}
 
-		await GetSessionAsync();
+		var res2 = await GetSessionAsync();
 
 		m_isLoggedIn = true;
 	}