diff --git a/LunaHook/CMakeLists.txt b/LunaHook/CMakeLists.txt index cb2490f..721c57b 100644 --- a/LunaHook/CMakeLists.txt +++ b/LunaHook/CMakeLists.txt @@ -51,4 +51,4 @@ target_precompile_headers(LunaHook REUSE_FROM pchhook) set_target_properties(LunaHook PROPERTIES OUTPUT_NAME "LunaHook${bitappendix}") -target_link_libraries(LunaHook Version Shlwapi pch minhook commonengine utils ${YY_Thunks_for_WinXP} ${Detours}) \ No newline at end of file +target_link_libraries(LunaHook Version httpapi Shlwapi pch minhook commonengine utils ${YY_Thunks_for_WinXP} ${Detours}) \ No newline at end of file diff --git a/LunaHook/engines/CMakeLists.txt b/LunaHook/engines/CMakeLists.txt index f529bb8..63c0e16 100644 --- a/LunaHook/engines/CMakeLists.txt +++ b/LunaHook/engines/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(commonengine mono/impl_mono.cpp mono/monoil2cpp.cpp mono/impl_il2cpp.cpp ppsspp/ppsspp.cpp mages/mages.cpp v8/v8.cpp python/python2.cpp python/python3.cpp python/python.cpp pchooks/pchooks.cpp lua/lua51.cpp) +add_library(commonengine mono/impl_mono.cpp mono/monoil2cpp.cpp mono/impl_il2cpp.cpp ppsspp/ppsspp.cpp mages/mages.cpp v8/v8.cpp v8/httpserver.cpp python/python2.cpp python/python3.cpp python/python.cpp pchooks/pchooks.cpp lua/lua51.cpp) target_precompile_headers(commonengine REUSE_FROM pchhook) diff --git a/LunaHook/engines/v8/httpserver.cpp b/LunaHook/engines/v8/httpserver.cpp new file mode 100644 index 0000000..3ba4dc9 --- /dev/null +++ b/LunaHook/engines/v8/httpserver.cpp @@ -0,0 +1,561 @@ +// https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/netds/http/HttpV2Server/main.c +/*++ + Copyright (c) 2002 - 2002 Microsoft Corporation. All Rights Reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF + ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A + PARTICULAR PURPOSE. + + THIS CODE IS NOT SUPPORTED BY MICROSOFT. + +--*/ + +#define SECURITY_WIN32 +#include +#include +#include +#define NUM_SCHEMES 2 +#define MAX_USERNAME_LENGTH 100 +#pragma warning(disable : 4127) // condition expression is constant + +// +// Macros. +// +#define INITIALIZE_HTTP_RESPONSE(resp, status, reason) \ + do \ + { \ + RtlZeroMemory((resp), sizeof(*(resp))); \ + (resp)->StatusCode = (status); \ + (resp)->pReason = (reason); \ + (resp)->ReasonLength = (USHORT)strlen(reason); \ + } while (FALSE) + +#define ADD_KNOWN_HEADER(Response, HeaderId, RawValue) \ + do \ + { \ + (Response).Headers.KnownHeaders[(HeaderId)].pRawValue = (RawValue); \ + (Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \ + (USHORT)strlen(RawValue); \ + } while (FALSE) + +#define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb)) +#define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr)) + +// +// Prototypes. +// +DWORD +DoReceiveRequests( + HANDLE hReqQueue); + +DWORD +SendHttpResponse( + IN HANDLE hReqQueue, + IN PHTTP_REQUEST pRequest); + +/***************************************************************************++ + +Routine Description: + main routine. + +Arguments: + argc - # of command line arguments. + argv - Arguments. + +Return Value: + Success/Failure. + +--***************************************************************************/ + +int cleanuphttp(HANDLE hReqQueue, HTTP_SERVER_SESSION_ID ssID, HTTP_URL_GROUP_ID urlGroupId) +{ + ULONG retCode; + // + // Call HttpRemoveUrl for all the URLs that we added. + // HTTP_URL_FLAG_REMOVE_ALL flag allows us to remove + // all the URLs registered on URL Group at once + // + if (!HTTP_IS_NULL_ID(&urlGroupId)) + { + + retCode = HttpRemoveUrlFromUrlGroup(urlGroupId, + NULL, + HTTP_URL_FLAG_REMOVE_ALL); + } + + // + // Close the Url Group + // + + if (!HTTP_IS_NULL_ID(&urlGroupId)) + { + retCode = HttpCloseUrlGroup(urlGroupId); + } + + // + // Close the serversession + // + + if (!HTTP_IS_NULL_ID(&urlGroupId)) + { + retCode = HttpCloseServerSession(ssID); + } + + // + // Close the Request Queue handle. + // + + if (hReqQueue) + { + retCode = HttpCloseRequestQueue(hReqQueue); + } + + // + // Call HttpTerminate. + // + HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); + return retCode; +} +auto makeserveronce(int port) +{ + ULONG retCode; + + HANDLE hReqQueue = NULL; + HTTP_SERVER_SESSION_ID ssID = HTTP_NULL_ID; + HTTP_URL_GROUP_ID urlGroupId = HTTP_NULL_ID; + HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_2; + HTTP_BINDING_INFO BindingProperty; + HTTP_TIMEOUT_LIMIT_INFO CGTimeout; + + auto url = std::wstring(L"http://127.0.0.1:") + std::to_wstring(port) + L"/fuck"; + // + // Initialize HTTP APIs. + // + + retCode = HttpInitialize( + HttpApiVersion, + HTTP_INITIALIZE_SERVER, // Flags + NULL // Reserved + ); + + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + + // + // Create a server session handle + // + + retCode = HttpCreateServerSession(HttpApiVersion, + &ssID, + 0); + + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + + // + // Create UrlGroup handle + // + + retCode = HttpCreateUrlGroup(ssID, + &urlGroupId, + 0); + + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + + // + // Create a request queue handle + // + + retCode = HttpCreateRequestQueue(HttpApiVersion, + (std::wstring(L"LUNA_INTERNAL_HTTP_QUEUE") + std::to_wstring(GetCurrentProcessId()) + L"_" + std::to_wstring(rand())).c_str(), + NULL, + 0, + &hReqQueue); + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + + BindingProperty.Flags.Present = 1; // Specifies that the property is present on UrlGroup + BindingProperty.RequestQueueHandle = hReqQueue; + + // + // Bind the request queue to UrlGroup + // + + retCode = HttpSetUrlGroupProperty(urlGroupId, + HttpServerBindingProperty, + &BindingProperty, + sizeof(BindingProperty)); + + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + + // + // Set EntityBody Timeout property on UrlGroup + // + + ZeroMemory(&CGTimeout, sizeof(HTTP_TIMEOUT_LIMIT_INFO)); + + CGTimeout.Flags.Present = 1; // Specifies that the property is present on UrlGroup + CGTimeout.EntityBody = 50; // The timeout is in secs + + retCode = HttpSetUrlGroupProperty(urlGroupId, + HttpServerTimeoutsProperty, + &CGTimeout, + sizeof(HTTP_TIMEOUT_LIMIT_INFO)); + + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + + // + // Add the URLs on URL Group + // The command line arguments represent URIs that we want to listen on. + // We will call HttpAddUrlToUrlGroup for each of these URIs. + // + // The URI is a fully qualified URI and MUST include the terminating '/' + // + + retCode = HttpAddUrlToUrlGroup(urlGroupId, + url.c_str(), + 0, + 0); + + if (retCode != NO_ERROR) + { + return std::tuple{false, hReqQueue, ssID, urlGroupId}; + } + return std::tuple{true, hReqQueue, ssID, urlGroupId}; +} +int GetRandomAvailablePort() +{ + static int xx = 9000 + GetCurrentProcessId() % 20000; + return xx++; + + // WSADATA wsaData; + // int result = WSAStartup(MAKEWORD(2, 2), &wsaData); + // if (result != 0) + // { + // return 0; + // } + + // // 创建一个 TCP 套接字 + // SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + // if (sock == INVALID_SOCKET) + // { + // WSACleanup(); + // return 0; + // } + + // // 绑定到随机端口 + // sockaddr_in addr; + // addr.sin_family = AF_INET; + // addr.sin_addr.s_addr = INADDR_ANY; + // addr.sin_port = 0; // 0 表示让系统自动选择一个可用端口 + + // result = bind(sock, (SOCKADDR *)&addr, sizeof(addr)); + // if (result == SOCKET_ERROR) + // { + // closesocket(sock); + // WSACleanup(); + // return 0; + // } + + // // 获取实际绑定的端口号 + // int addrLen = sizeof(addr); + // result = getsockname(sock, (SOCKADDR *)&addr, &addrLen); + // if (result == SOCKET_ERROR) + // { + // closesocket(sock); + // WSACleanup(); + // return 0; + // } + + // // 关闭套接字 + // closesocket(sock); + // WSACleanup(); + + // // 返回实际绑定的端口号 + // return ntohs(addr.sin_port); +} + +int makehttpgetserverinternal() +{ + while (1) + { + auto port = GetRandomAvailablePort(); + auto [succ, hReqQueue, ssID, urlGroupId] = makeserveronce(port); + if (!succ) + { + cleanuphttp(hReqQueue, ssID, urlGroupId); + continue; + } + std::thread([=]() + { + // Loop while receiving requests + DoReceiveRequests(hReqQueue); + cleanuphttp(hReqQueue, ssID, urlGroupId); }) + .detach(); + return port; + } +} +// int main() +// { +// wprintf(L"%d", makehttpgetserverinternal()); +// Sleep(999999); +// } +/***************************************************************************++ + +Routine Description: + The routine to receive a request. This routine calls the corresponding + routine to deal with the response. + +Arguments: + hReqQueue - Handle to the request queue. + +Return Value: + Success/Failure. + +--***************************************************************************/ + +DWORD +DoReceiveRequests( + IN HANDLE hReqQueue) +{ + ULONG result; + HTTP_REQUEST_ID requestId; + DWORD bytesRead; + PHTTP_REQUEST pRequest; + PCHAR pRequestBuffer; + ULONG RequestBufferLength; + + // + // Allocate a 2K buffer. Should be good for most requests, we'll grow + // this if required. We also need space for a HTTP_REQUEST structure. + // + RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; + pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength); + + if (pRequestBuffer == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + pRequest = (PHTTP_REQUEST)pRequestBuffer; + + // + // Wait for a new request -- This is indicated by a NULL request ID. + // + + HTTP_SET_NULL_ID(&requestId); + + for (;;) + { + RtlZeroMemory(pRequest, RequestBufferLength); + + result = HttpReceiveHttpRequest( + hReqQueue, // Req Queue + requestId, // Req ID + 0, // Flags + pRequest, // HTTP request buffer + RequestBufferLength, // req buffer length + &bytesRead, // bytes received + NULL // LPOVERLAPPED + ); + + if (NO_ERROR == result) + { + // + // Worked! + // + // switch (pRequest->Verb) + // { + // case HttpVerbGET: + result = SendHttpResponse( + hReqQueue, + pRequest); + + // case HttpVerbPOST: + // default: + + // if (result != NO_ERROR) + // { + // break; + // } + + // + // Reset the Request ID so that we pick up the next request. + // + HTTP_SET_NULL_ID(&requestId); + } + else if (result == ERROR_MORE_DATA) + { + // + // The input buffer was too small to hold the request headers + // We have to allocate more buffer & call the API again. + // + // When we call the API again, we want to pick up the request + // that just failed. This is done by passing a RequestID. + // + // This RequestID is picked from the old buffer. + // + requestId = pRequest->RequestId; + + // + // Free the old buffer and allocate a new one. + // + RequestBufferLength = bytesRead; + FREE_MEM(pRequestBuffer); + pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength); + + if (pRequestBuffer == NULL) + { + result = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + pRequest = (PHTTP_REQUEST)pRequestBuffer; + } + else if (ERROR_CONNECTION_INVALID == result && + !HTTP_IS_NULL_ID(&requestId)) + { + // The TCP connection got torn down by the peer when we were + // trying to pick up a request with more buffer. We'll just move + // onto the next request. + + HTTP_SET_NULL_ID(&requestId); + } + else + { + break; + } + + } // for(;;) + + if (pRequestBuffer) + { + FREE_MEM(pRequestBuffer); + } + + return result; +} + +/***************************************************************************++ + +Routine Description: + The routine sends a HTTP response. + +Arguments: + hReqQueue - Handle to the request queue. + pRequest - The parsed HTTP request. + StatusCode - Response Status Code. + pReason - Response reason phrase. + pEntityString - Response entity body. + +Return Value: + Success/Failure. + +--***************************************************************************/ + +std::string urlDecode(const std::string &encoded) +{ + std::string decoded; + for (size_t i = 0; i < encoded.size(); i++) + { + if (encoded[i] == '%') + { + char ch = std::stoi(encoded.substr(i + 1, 2), 0, 16); + decoded += ch; + i = i + 2; + } + else if (encoded[i] == '+') + { + decoded += ' '; + } + else + { + decoded += encoded[i]; + } + } + return decoded; +} + +#pragma optimize("", off) +const wchar_t *LUNA_CONTENTBYPASS(const wchar_t *_) +{ + return _; +} +#pragma optimize("", on) + +DWORD +SendHttpResponse( + IN HANDLE hReqQueue, + IN PHTTP_REQUEST pRequest) +{ + HTTP_RESPONSE response; + HTTP_DATA_CHUNK dataChunk; + DWORD result; + DWORD bytesSent; + USHORT StatusCode = 200; + PSTR pReason = "OK"; + + // + // Initialize the HTTP response structure. + // + INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason); + + // + // Add a known header. + // + ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html"); + std::string url(pRequest->pRawUrl, pRequest->RawUrlLength); + auto fnd = url.find('?'); + + if (fnd != url.npos) + { + url = url.substr(fnd + 1); + url = urlDecode(url); + url = WideStringToString(LUNA_CONTENTBYPASS(StringToWideString(url).c_str())); + // + // Add an entity chunk + // + dataChunk.DataChunkType = HttpDataChunkFromMemory; + dataChunk.FromMemory.pBuffer = (PVOID)url.c_str(); + dataChunk.FromMemory.BufferLength = (ULONG)url.size(); + + response.EntityChunkCount = 1; + response.pEntityChunks = &dataChunk; + } + + // + // Since we are sending all the entity body in one call, we don't have + // to specify the Content-Length. + // + + result = HttpSendHttpResponse( + hReqQueue, // ReqQueueHandle + pRequest->RequestId, // Request ID + 0, // Flags + &response, // HTTP response + NULL, // pReserved1 + &bytesSent, // bytes sent (OPTIONAL) + NULL, // pReserved2 (must be NULL) + 0, // Reserved3 (must be 0) + NULL, // LPOVERLAPPED (OPTIONAL) + NULL // pReserved4 (must be NULL) + ); + + return result; +} \ No newline at end of file diff --git a/LunaHook/engines/v8/v8.cpp b/LunaHook/engines/v8/v8.cpp index 79e8d9f..dc1b3ed 100644 --- a/LunaHook/engines/v8/v8.cpp +++ b/LunaHook/engines/v8/v8.cpp @@ -1,8 +1,41 @@ #include "v8.h" + +int makehttpgetserverinternal(); +const wchar_t *LUNA_CONTENTBYPASS(const wchar_t *_); namespace { constexpr auto magicsend = L"\x01LUNAFROMJS\x01"; constexpr auto magicrecv = L"\x01LUNAFROMHOST\x01"; +} +namespace +{ + + void parsebefore(wchar_t *text, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) + { + if (startWith(text, magicsend)) + { + text += wcslen(magicsend); + auto spl = wcschr(text, L'\x03'); + strcpy(hp->name, wcasta(std::wstring(text, spl - text)).c_str()); + text = spl + 1; + spl = wcschr(text, L'\x02'); + *split = std::stoi(std::wstring(text, spl - text)); + text = spl + 1; + *data = (uintptr_t)text; + *len = wcslen(text) * 2; + } + } + std::wstring parseafter(void *data, size_t len) + { + std::wstring transwithfont = magicrecv; + transwithfont += embedsharedmem->fontFamily; + transwithfont += L'\x02'; + transwithfont += std::wstring((wchar_t *)data, len / 2); + return transwithfont; + } +} +namespace +{ bool hookClipboard() { HookParam hp; @@ -11,28 +44,12 @@ namespace hp.text_fun = [](hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) { HGLOBAL hClipboardData = (HGLOBAL)stack->ARG2; - auto text = (wchar_t *)GlobalLock(hClipboardData); - if (startWith(text, magicsend)) - { - text += wcslen(magicsend); - auto spl = wcschr(text, L'\x03'); - strcpy(hp->name, wcasta(std::wstring(text, spl - text)).c_str()); - text = spl + 1; - spl = wcschr(text, L'\x02'); - *split = std::stoi(std::wstring(text, spl - text)); - text = spl + 1; - *data = (uintptr_t)text; - *len = wcslen(text) * 2; - } - + parsebefore((wchar_t *)GlobalLock(hClipboardData), hp, data, split, len); GlobalUnlock(hClipboardData); }; hp.hook_after = [](hook_stack *s, void *data, size_t len) { - std::wstring transwithfont = magicrecv; - transwithfont += embedsharedmem->fontFamily; - transwithfont += L'\x02'; - transwithfont += std::wstring((wchar_t *)data, len / 2); + std::wstring transwithfont = parseafter(data, len); HGLOBAL hClipboardData = GlobalAlloc(GMEM_MOVEABLE, transwithfont.size() * 2 + 2); auto pchData = (wchar_t *)GlobalLock(hClipboardData); wcscpy(pchData, (wchar_t *)transwithfont.c_str()); @@ -42,6 +59,27 @@ namespace return NewHook(hp, "nwjs/electron rpgmakermv/tyranoscript"); } } +namespace +{ + bool hook_LUNA_CONTENTBYPASS() + { + HookParam hp; + hp.address = (uintptr_t)LUNA_CONTENTBYPASS; + hp.type = USING_STRING | NO_CONTEXT | CODEC_UTF16 | EMBED_ABLE | EMBED_BEFORE_SIMPLE; + hp.text_fun = [](hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) + { + parsebefore((wchar_t *)stack->ARG1, hp, data, split, len); + }; + hp.hook_after = [](hook_stack *s, void *data, size_t len) + { + std::wstring transwithfont = parseafter(data, len); + auto news = new wchar_t[transwithfont.size() + 1]; + wcscpy(news, transwithfont.c_str()); + s->ARG1 = (uintptr_t)news; + }; + return NewHook(hp, "nwjs/electron rpgmakermv/tyranoscript"); + } +} namespace v8script { HMODULE hmodule; @@ -112,7 +150,26 @@ namespace v8script } } } - NewFromUtf8(&v8string, isolate, FormatString(LoadResData(L"lunajspatch", L"JSSOURCE").c_str(), is_packed).c_str(), 1, -1); + auto port = 0; + + auto useclipboard = !std::filesystem::exists(std::filesystem::path(getModuleFilename().value()).replace_filename("disable.clipboard")); + auto usehttp = !std::filesystem::exists(std::filesystem::path(getModuleFilename().value()).replace_filename("disable.http")); + if (usehttp) + { + port = makehttpgetserverinternal(); + ConsoleOutput("%d %d", GetCurrentProcessId(), port); + hook_LUNA_CONTENTBYPASS(); + dont_detach = true; + } + if (useclipboard) + { + hookClipboard(); + } + auto lunajspatch = LoadResData(L"lunajspatch", L"JSSOURCE"); + strReplace(lunajspatch, "IS_PACKED", std::to_string(is_packed)); + strReplace(lunajspatch, "IS_USECLIPBOARD", std::to_string(useclipboard)); + strReplace(lunajspatch, "INTERNAL_HTTP_PORT", std::to_string(port)); + NewFromUtf8(&v8string, isolate, lunajspatch.c_str(), 1, -1); ConsoleOutput("v8string %p", v8string); if (v8string == 0) return; @@ -168,7 +225,8 @@ namespace v8script if (RequestInterrupt == 0) return false; - RequestInterrupt(isolate, _interrupt_function, 0); + + RequestInterrupt(isolate, _interrupt_function, nullptr); return true; } @@ -294,14 +352,6 @@ namespace return succ; } } -bool tryhookv8_internal(HMODULE hm) -{ - auto succ = hookstring(hm); - if (!std::filesystem::exists(std::filesystem::path(getModuleFilename().value()).replace_filename("disable.clipboard"))) - if (v8script::v8runscript(hm)) - succ |= hookClipboard(); - return succ; -} bool tryhookv8() { for (const wchar_t *moduleName : {(const wchar_t *)NULL, L"node.dll", L"nw.dll"}) @@ -309,7 +359,8 @@ bool tryhookv8() auto hm = GetModuleHandleW(moduleName); if (hm == 0) continue; - bool ok = tryhookv8_internal(hm); + auto ok = hookstring(hm); + ok |= v8script::v8runscript(hm); if (ok) return true; } diff --git a/LunaHook/resource/lunajspatch.js b/LunaHook/resource/lunajspatch.js index 44fb158..db0c890 100644 --- a/LunaHook/resource/lunajspatch.js +++ b/LunaHook/resource/lunajspatch.js @@ -1,7 +1,9 @@ var fontface = ''; var magicsend = '\x01LUNAFROMJS\x01' var magicrecv = '\x01LUNAFROMHOST\x01' -var is_packed = %d +var is_packed = IS_PACKED +var is_useclipboard = IS_USECLIPBOARD +var internal_http_port = INTERNAL_HTTP_PORT function splitfonttext(transwithfont) { if (transwithfont.substr(0, magicsend.length) == magicsend) //not trans { @@ -20,41 +22,77 @@ function splitfonttext(transwithfont) { return transwithfont; } } +function syncquery(s) { + if (internal_http_port == 0) { throw new Error('') } + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://127.0.0.1:' + internal_http_port + '/fuck?' + s, false); + xhr.send(); + if (xhr.status === 200) { + return xhr.responseText; + } else { + throw new Error('') + } + +} +function isEmptyString(str) { + return str === null || str === undefined || str.length == 0; +} function clipboardsender(name, s_raw, lpsplit) { //magic split \x02 text + if (isEmptyString(s_raw)) + return s_raw s = magicsend + name + '\x03' + lpsplit.toString() + '\x02' + s_raw; try { + if (!is_useclipboard) { throw new Error('') } const _clipboard = require('nw.gui').Clipboard.get(); _clipboard.set(s, 'text'); transwithfont = _clipboard.get('text'); } catch (err) { try { + if (!is_useclipboard) { throw new Error('') } const clipboard = require('electron').clipboard; clipboard.writeText(s); transwithfont = clipboard.readText(); } catch (err2) { - return s_raw; + + try { + + transwithfont = syncquery(s) + } + catch (err3) { + return s_raw; + } } } if (transwithfont.length == 0) return s_raw; return splitfonttext(transwithfont) } -function clipboardsender_only_send(name, s, lpsplit) { +function clipboardsender_only_send(name, s_raw, lpsplit) { //magic split \x02 text - s = magicsend + name + '\x03' + lpsplit.toString() + '\x02' + s; + if (isEmptyString(s_raw)) + return s_raw + s = magicsend + name + '\x03' + lpsplit.toString() + '\x02' + s_raw; try { + if (!is_useclipboard) { throw new Error('') } const _clipboard = require('nw.gui').Clipboard.get(); _clipboard.set(s, 'text'); } catch (err) { try { + if (!is_useclipboard) { throw new Error('') } const clipboard = require('electron').clipboard; clipboard.writeText(s); } catch (err2) { + try { + syncquery(s) + } + catch (err3) { + + } } } }