diff --git a/CMakeLists.txt b/CMakeLists.txt index 738d1ca..b16e331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ include(generate_product_version) set(VERSION_MAJOR 3) set(VERSION_MINOR 15) -set(VERSION_PATCH 0) +set(VERSION_PATCH 1) set(VERSION_REVISION 0) if(BUILD_CORE) diff --git a/LunaHook/engines/v8/httpserver.cpp b/LunaHook/engines/v8/httpserver.cpp index 95f93e8..283794c 100644 --- a/LunaHook/engines/v8/httpserver.cpp +++ b/LunaHook/engines/v8/httpserver.cpp @@ -471,29 +471,6 @@ Return Value: --***************************************************************************/ -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 *_) { @@ -507,57 +484,114 @@ SendHttpResponse( IN PHTTP_REQUEST pRequest) { HTTP_RESPONSE response; - HTTP_DATA_CHUNK dataChunk; DWORD result; DWORD bytesSent; - USHORT StatusCode = 200; - PSTR pReason = "OK"; - + ULONG BytesRead; + HTTP_DATA_CHUNK dataChunk; + std::string recv; + std::string buff; + buff.resize(2048); + bool recving = true; // // Initialize the HTTP response structure. // - INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason); + INITIALIZE_HTTP_RESPONSE(&response, 200, "OK"); // - // Add a known header. + // For POST, we'll echo back the entity that we got from the client. // - ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html"); - std::string url(pRequest->pRawUrl, pRequest->RawUrlLength); - auto fnd = url.find('?'); - - if (fnd != url.npos) + // NOTE: If we had passed the HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY + // flag with HttpReceiveHttpRequest(), the entity would have + // been a part of HTTP_REQUEST (using the pEntityChunks field). + // Since we have not passed that flag, we can be assured that + // there are no entity bodies in HTTP_REQUEST. + // + if (pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS) { - url = url.substr(fnd + 1); - url = urlDecode(url); - url = WideStringToString(LUNA_CONTENTBYPASS(StringToWideString(url).c_str())); + // The entity body is send over multiple calls. Let's collect all + // of these in a file & send it back. We'll create a temp file // - // 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; + do + { + // + // Read the entity chunk from the request. + // + BytesRead = 0; + result = HttpReceiveRequestEntityBody( + hReqQueue, + pRequest->RequestId, + 0, + buff.data(), + buff.capacity(), + &BytesRead, + NULL); + switch (result) + { + case NO_ERROR: + case ERROR_HANDLE_EOF: + + if (BytesRead != 0) + { + recv += buff.substr(0, BytesRead); + } + if (result == ERROR_HANDLE_EOF) + recving = false; + break; + + default: + recving = false; + } + + } while (recving); } + if (recv.size()) + recv = WideStringToString(LUNA_CONTENTBYPASS(StringToWideString(recv).c_str())); + if (recv.size()) + { + ADD_KNOWN_HEADER( + response, + HttpHeaderContentLength, + std::to_string(recv.size()).c_str()); + } + result = + HttpSendHttpResponse( + hReqQueue, // ReqQueueHandle + pRequest->RequestId, // Request ID + recv.size() ? HTTP_SEND_RESPONSE_FLAG_MORE_DATA : 0, + &response, // HTTP response + NULL, // pReserved1 + &bytesSent, // bytes sent (optional) + NULL, // pReserved2 + 0, // Reserved3 + NULL, // LPOVERLAPPED + NULL // pReserved4 + ); + if (result != NO_ERROR) + { + return result; + } + if (!recv.size()) + return result; // - // Since we are sending all the entity body in one call, we don't have - // to specify the Content-Length. + // Send entity body from a file handle. // + dataChunk.DataChunkType = HttpDataChunkFromMemory; + dataChunk.FromMemory.pBuffer = (PVOID)recv.c_str(); + dataChunk.FromMemory.BufferLength = (ULONG)recv.size(); - 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) - ); + result = HttpSendResponseEntityBody( + hReqQueue, + pRequest->RequestId, + 0, // This is the last send. + 1, // Entity Chunk Count. + &dataChunk, + NULL, + NULL, + 0, + NULL, + NULL); return result; } \ No newline at end of file diff --git a/LunaHook/engines/v8/v8.cpp b/LunaHook/engines/v8/v8.cpp index b57acc2..8381d53 100644 --- a/LunaHook/engines/v8/v8.cpp +++ b/LunaHook/engines/v8/v8.cpp @@ -8,6 +8,12 @@ namespace constexpr auto magicrecv = L"\x01LUNAFROMHOST\x01"; } namespace +{ + bool useclipboard = true; + bool usehttp = true; + int usehttp_port = 0; +} +namespace { void parsebefore(wchar_t *text, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) @@ -150,25 +156,11 @@ namespace v8script } } } - 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)); + strReplace(lunajspatch, "INTERNAL_HTTP_PORT", std::to_string(usehttp_port)); NewFromUtf8(&v8string, isolate, lunajspatch.c_str(), 1, -1); ConsoleOutput("v8string %p", v8string); if (v8string == 0) @@ -363,10 +355,24 @@ bool tryhookv8() auto hm = GetModuleHandleW(moduleName); if (hm == 0) continue; - auto ok = hookstring(hm); - ok |= v8script::v8runscript(hm); - if (ok) - return true; + if (hookstring(hm)) + { + useclipboard = !std::filesystem::exists(std::filesystem::path(getModuleFilename().value()).replace_filename("disable.clipboard")); + usehttp = !std::filesystem::exists(std::filesystem::path(getModuleFilename().value()).replace_filename("disable.http")); + if (usehttp) + { + usehttp_port = makehttpgetserverinternal(); + ConsoleOutput("%d %d", GetCurrentProcessId(), usehttp_port); + hook_LUNA_CONTENTBYPASS(); + dont_detach = true; + } + if (useclipboard) + { + hookClipboard(); + } + + return v8script::v8runscript(hm); + } } return false; } diff --git a/LunaHook/resource/lunajspatch.js b/LunaHook/resource/lunajspatch.js index fd01b13..b8914f4 100644 --- a/LunaHook/resource/lunajspatch.js +++ b/LunaHook/resource/lunajspatch.js @@ -12,7 +12,6 @@ function splitfonttext(transwithfont) { } else if (transwithfont.substr(0, magicrecv.length) == magicrecv) { transwithfont = transwithfont.substr(magicrecv.length) - //magic font \x02 text split = transwithfont.search('\x02') fontface = transwithfont.substr(0, split) text = transwithfont.substr(split + 1) @@ -22,61 +21,40 @@ function splitfonttext(transwithfont) { return transwithfont; } } -function syncquery(s) { - if (internal_http_port == 0) { throw new Error('') } - var xhr = new XMLHttpRequest(); - var url = 'http://127.0.0.1:' + internal_http_port + '/fuck?' + s - xhr.open('GET', url, false); - xhr.send(); - if (xhr.status === 200) { - return xhr.responseText;//解析这个会导致v8::String::Length的v8StringUtf8Length出现错误,但不影响。 - } else { - throw new Error('') - } - -} -function makecomplexs(name, s_raw, lpsplit) { - return magicsend + name + '\x03' + lpsplit.toString() + '\x02' + s_raw; -} -function cppjsio(s) { - try { - return syncquery(s) - } - catch (err) { - try { - if (!is_useclipboard) { throw new Error('') } - const _clipboard = require('nw.gui').Clipboard.get(); - _clipboard.set(s, 'text'); - return _clipboard.get('text'); - } - catch (err2) { - try { - if (!is_useclipboard) { throw new Error('') } - const clipboard = require('electron').clipboard; - clipboard.writeText(s); - return clipboard.readText(); - } - catch (err3) { - return s_raw; - } - } - } -} -function clipboardsender(name, s_raw, lpsplit) { - //magic split \x02 text +function cppjsio(name, s_raw, lpsplit) { if (!s_raw) return s_raw - transwithfont = cppjsio(makecomplexs(name, s_raw, lpsplit)) - if (transwithfont.length == 0) return s_raw; + transwithfont = '' + s = magicsend + name + '\x03' + lpsplit.toString() + '\x02' + s_raw; + if (internal_http_port) { + var xhr = new XMLHttpRequest(); + var url = 'http://127.0.0.1:' + internal_http_port + '/fuck' + xhr.open('POST', url, false); + xhr.send(s); + if (xhr.status === 200) { + transwithfont = xhr.responseText; + } + } + else if (is_useclipboard) { + try { + const _clipboard = require('nw.gui').Clipboard.get(); + _clipboard.set(s, 'text'); + transwithfont = _clipboard.get('text'); + } + catch (err) { + try { + const clipboard = require('electron').clipboard; + clipboard.writeText(s); + transwithfont = clipboard.readText(); + } + catch (err2) { + } + } + } + if (!transwithfont) return s_raw; return splitfonttext(transwithfont) } -function clipboardsender_only_send(name, s_raw, lpsplit) { - //magic split \x02 text - if (!s_raw) - return s_raw - cppjsio(makecomplexs(name, s_raw, lpsplit)) -} function rpgmakerhook() { if (Window_Message.prototype.originstartMessage) { } @@ -99,7 +77,7 @@ function rpgmakerhook() { setInterval(function () { for (lpsplit in Bitmap.prototype.collectstring) { if (Bitmap.prototype.collectstring[lpsplit].length) { - clipboardsender_only_send('rpgmakermv', Bitmap.prototype.collectstring[lpsplit], lpsplit) + cppjsio('rpgmakermv', Bitmap.prototype.collectstring[lpsplit], lpsplit) Bitmap.prototype.collectstring[lpsplit] = '' } } @@ -121,12 +99,12 @@ function rpgmakerhook() { } Window_Message.prototype.startMessage = function () { gametext = $gameMessage.allText(); - resp = clipboardsender('rpgmakermv', gametext, 0); + resp = cppjsio('rpgmakermv', gametext, 0); $gameMessage._texts = [resp] this.originstartMessage(); }; Window_Base.prototype.drawText = function (text, x, y, maxWidth, align) { - text = clipboardsender('rpgmakermv', text, 1) + text = cppjsio('rpgmakermv', text, 1) return this.drawText_origin(text, x, y, maxWidth, align) } Window_Base.prototype.lastcalltime = 0 @@ -135,7 +113,7 @@ function rpgmakerhook() { __now = new Date().getTime() Window_Base.prototype.lastcalltime = __now if (__now - __last > 100) - text = clipboardsender('rpgmakermv', text, 2) + text = cppjsio('rpgmakermv', text, 2) else { Bitmap.prototype.collectstring[2] += text; } @@ -150,7 +128,7 @@ function tyranohook() { tyrano.plugin.kag.tag.chara_ptext.startorigin = tyrano.plugin.kag.tag.chara_ptext.start; tyrano.plugin.kag.tag.text.start = function (pm) { if (1 != this.kag.stat.is_script && 1 != this.kag.stat.is_html) { - pm.val = clipboardsender('tyranoscript', pm.val, 0); + pm.val = cppjsio('tyranoscript', pm.val, 0); if (fontface) { this.kag.stat.font.face = fontface } @@ -158,7 +136,7 @@ function tyranohook() { return this.originstart(pm) } tyrano.plugin.kag.tag.chara_ptext.start = function (pm) { - pm.name = clipboardsender('tyranoscript', pm.name, 1) + pm.name = cppjsio('tyranoscript', pm.name, 1) return this.startorigin(pm) } }