From c0f3b0b8d65601d72482e01c237f8e83582937e2 Mon Sep 17 00:00:00 2001 From: nift4 Date: Sat, 15 Jun 2024 12:38:25 +0200 Subject: [PATCH 1/5] Ignore .DS_Store file This is automatically created basically everywhere on macOS. --- .gitignore | 1 + www/modloader/early_loader.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0ac4c09..a6adf6a 100755 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ snapshot_blob.bin locales/ pnacl/ swiftshader/ +.DS_Store # BASE OMORI STUFF package.json diff --git a/www/modloader/early_loader.js b/www/modloader/early_loader.js index 722236d..ef6281e 100644 --- a/www/modloader/early_loader.js +++ b/www/modloader/early_loader.js @@ -816,8 +816,12 @@ mod = new DirectoryMod(path.join(base, "mods", mod_file)); } else { if (!mod_file.endsWith(".zip")) { - window._logLine("| [ERROR] Skipping, not directory and extension isn't zip"); - errorCount++; + if (mod_file === ".DS_Store") { + window._logLine("| [INFO] Skipping .DS_Store"); + } else { + window._logLine("| [ERROR] Skipping, not directory and extension isn't zip"); + errorCount++; + } continue; } mod = new ZipMod(path.join(base, "mods", mod_file)); From 591aab89114ae350b92b183386dd81e2aa60523a Mon Sep 17 00:00:00 2001 From: nift4 Date: Sun, 16 Jun 2024 09:53:08 +0200 Subject: [PATCH 2/5] Revert "Deprecate "modern" NW." This reverts commit 957e0144af85daf7411edde49de6a89bb63c60ca. --- www/modloader/vfs_web.js | 303 ++++++++++++++++++++++++++------------- 1 file changed, 200 insertions(+), 103 deletions(-) diff --git a/www/modloader/vfs_web.js b/www/modloader/vfs_web.js index bfb0f1a..d642b35 100644 --- a/www/modloader/vfs_web.js +++ b/www/modloader/vfs_web.js @@ -1,128 +1,225 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { - if ($modLoader.$nwMajor >= 45) { alert("Versions of NW newer than 0.45.0 are not supported!"); throw new Error();} + if ($modLoader.$nwMajor < 45) { + async function buildResponseBody(data) { + $modLoader.$vfsTrace("WEB REQUEST " + JSON.stringify(data)); + let url = new URL(data.request.url); - async function buildResponseBody(data) { - $modLoader.$vfsTrace("WEB REQUEST " + JSON.stringify(data)); - let url = new URL(data.request.url); + if ($modLoader.isInTestMode) { + if (url.origin === window.location.origin) { + let vfsPath = url.pathname; + try { + let rdata = await _vfs_resolve_file(vfsPath); + let hS = ""; + for (let header in data.responseHeaders) { + hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; + } - if ($modLoader.isInTestMode) { - if (url.origin === window.location.origin) { - let vfsPath = url.pathname; - try { - let rdata = await _vfs_resolve_file(vfsPath); - let hS = ""; - for (let header in data.responseHeaders) { - hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; + let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; + responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); + return { + interceptionId: data.interceptionId, + rawResponse: responseBody + }; + } catch(e) { + window._logLine("Error occured when building response body: " + e.stack); + return { + interceptionId: data.interceptionId + } } - - let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; - responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); - return { - interceptionId: data.interceptionId, - rawResponse: responseBody - }; - } catch(e) { - window._logLine("Error occured when building response body: " + e.stack); + } else { return { interceptionId: data.interceptionId } } } else { - return { - interceptionId: data.interceptionId - } - } - } else { - if (url.origin === window.location.origin && url.pathname.startsWith("/www/")) { - let vfsPath = url.pathname.replace(/^[\/\\]*www[\/\\]*/, ""); - try { - let rdata = await _vfs_resolve_file(vfsPath); - let hS = ""; - for (let header in data.responseHeaders) { - hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; - } + if (url.origin === window.location.origin && url.pathname.startsWith("/www/")) { + let vfsPath = url.pathname.replace(/^[\/\\]*www[\/\\]*/, ""); + try { + let rdata = await _vfs_resolve_file(vfsPath); + let hS = ""; + for (let header in data.responseHeaders) { + hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; + } - let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; - responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); - return { - interceptionId: data.interceptionId, - rawResponse: responseBody - }; - } catch(e) { - window._logLine("Error occured when building response body: " + e.stack); + let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; + responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); + return { + interceptionId: data.interceptionId, + rawResponse: responseBody + }; + } catch(e) { + window._logLine("Error occured when building response body: " + e.stack); + return { + interceptionId: data.interceptionId + } + } + } else { return { interceptionId: data.interceptionId } } - } else { - return { - interceptionId: data.interceptionId - } } } - } - return new Promise(resolve => { - window._logLine("Gathering chrome devtools remote debugging candidates"); - chrome.debugger.getTargets((t) => { - let debugee = {targetId: ""}; - for (let candidate of t) { - if (candidate.url.endsWith("index.html")) { - debugee.targetId = candidate.id; - } - } - - chrome.debugger.detach(debugee, () => { - if(chrome.runtime.lastError) { - console.warn(chrome.runtime.lastError.message); + return new Promise(resolve => { + window._logLine("Gathering chrome devtools remote debugging candidates"); + chrome.debugger.getTargets((t) => { + let debugee = {targetId: ""}; + for (let candidate of t) { + if (candidate.url.endsWith("index.html")) { + debugee.targetId = candidate.id; + } } - chrome.debugger.attach(debugee, "1.2", () => { - window._logLine("[DEVTOOLS] Successfully attached!"); - chrome.debugger.onEvent.addListener(async (debugee, event, data) => { - if (event === "Network.requestIntercepted") { - chrome.debugger.sendCommand(debugee, "Network.continueInterceptedRequest", await buildResponseBody(data)); + chrome.debugger.detach(debugee, () => { + if(chrome.runtime.lastError) { + console.warn(chrome.runtime.lastError.message); + } + + chrome.debugger.attach(debugee, "1.2", () => { + window._logLine("[DEVTOOLS] Successfully attached!"); + chrome.debugger.onEvent.addListener(async (debugee, event, data) => { + if (event === "Network.requestIntercepted") { + chrome.debugger.sendCommand(debugee, "Network.continueInterceptedRequest", await buildResponseBody(data)); + } + }); + if (!$modLoader.isInTestMode) { + chrome.debugger.sendCommand( + debugee, + "Network.setRequestInterception", + { + enabled: true, + patterns: [ + { + urlPattern: window.location.origin + "/www/*", + interceptionStage: "HeadersReceived" + } + ] + } + ); + } else { + chrome.debugger.sendCommand( + debugee, + "Network.setRequestInterception", + { + enabled: true, + patterns: [ + { + urlPattern: window.location.origin + "/*", + interceptionStage: "HeadersReceived" + } + ] + } + ); } + + setTimeout(resolve, 100); }); - if (!$modLoader.isInTestMode) { - chrome.debugger.sendCommand( - debugee, - "Network.setRequestInterception", - { - enabled: true, - patterns: [ - { - urlPattern: window.location.origin + "/www/*", - interceptionStage: "HeadersReceived" - } - ] - } - ); - } else { - chrome.debugger.sendCommand( - debugee, - "Network.setRequestInterception", - { - enabled: true, - patterns: [ - { - urlPattern: window.location.origin + "/*", - interceptionStage: "HeadersReceived" - } - ] - } - ); - } - setTimeout(resolve, 100); + window.__unload_web_vfs = function() { + chrome.debugger.detach(debugee); + }; + window.addEventListener("beforeunload", function(e) { + chrome.debugger.detach(debugee); + }); }); + }) + }); + } else { + $modLoader.$log("[VFS_WEB] Starting HTTP server"); - window.__unload_web_vfs = function() { - chrome.debugger.detach(debugee); - }; - window.addEventListener("beforeunload", function(e) { - chrome.debugger.detach(debugee); - }); + let SERVER_KEY = require('crypto').randomBytes(32).toString('hex'); + let TEST_KEY = require('crypto').randomBytes(32).toString('hex'); + let testKeyChecker; + const path = require('path'); + const base = path.dirname(process.mainModule.filename); + const urlTester = new RegExp(`^/${SERVER_KEY}`); + async function requestListener(req, res) { + try { + if (req.url === `/${SERVER_KEY}/${TEST_KEY}`) { + res.setHeader("Content-Type", "application/json"); + res.writeHead(200); + res.end('{"ready": true}'); + + $modLoader.$vfsTrace(`[VFS_WEB] ${TEST_KEY}`); + $modLoader.$log(`[VFS_WEB] Got Test key. Successfully authenticated client.`); + + return; + } + + if (!urlTester.test(req.url)) { + res.writeHead(400); + res.end('Server key not present!'); + + $modLoader.$vfsTrace(`[VFS_WEB] Unauthorized request to VFS! Path: ${req.url}`); + + return; + } + + let requestPath = req.url.split(new RegExp(`^/${SERVER_KEY}`))[1]; + if (requestPath.startsWith("/www/")) { + requestPath = requestPath.replace(/^\/www/, ""); + } + + try { + const data = await _vfs_resolve_file(requestPath); + + let ext = requestPath.match(/\.([^\.]*)$/)[1]; + + let type = Mime.getType(ext); + + res.setHeader("Content-Type", type); + res.setHeader("Access-Control-Allow-Origin", window.location.origin); + res.writeHead(200); + res.end(data); + + $modLoader.$vfsTrace(`[VFS_WEB] Got request ${req.url} (${requestPath})`); + } catch(E) { + res.writeHead(404); + res.end(''); + } + } catch(E) { + console.log(E); + } + } + + return new Promise(async (resolve, reject) => { + const [server, port] = await new Promise((listening, _) => { + const http = require('http'); + + let server = http.createServer(requestListener); + + server.listen(0, "127.0.0.1", undefined, () => void listening([server, server.address().port])); }); - }) - }); + + $modLoader.$log(`[VFS_WEB] Proxy server listening on Port 127.0.0.1:${port}`); + + function beforeRequestInterceptor(details) { + let u = new URL(details.url); + if (u.pathname === "/www/modloader/one_loader_sw.js") return null; + return {redirectUrl: `http://127.0.0.1:${port}/${SERVER_KEY}${u.pathname}`} + } + + chrome.webRequest.onBeforeRequest.addListener(beforeRequestInterceptor, { + urls: [ + window.location.origin + "/www/*", + window.location.origin + "/" + TEST_KEY + ] + }, ["blocking"]) + + window.addEventListener("beforeunload", function(e) { + chrome.webRequest.onBeforeRequest.removeListener(beforeRequestInterceptor); + server.close(); + }); + + testKeyChecker = setInterval(() => { + fetch(`/${TEST_KEY}`).then(a => a.json()).then(a => { + if (a && a.ready === true) { + console.log("test key done"); + resolve(); + clearInterval(testKeyChecker); + } + }).catch(e => {}); + }, 100); + }); + } } \ No newline at end of file From 2ef6b314bd8b2a04de1f130711fb904d736746f5 Mon Sep 17 00:00:00 2001 From: Rph Date: Sun, 16 Oct 2022 00:20:04 +0200 Subject: [PATCH 3/5] Support range headers --- www/modloader/vfs_web.js | 59 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/www/modloader/vfs_web.js b/www/modloader/vfs_web.js index d642b35..22aefed 100644 --- a/www/modloader/vfs_web.js +++ b/www/modloader/vfs_web.js @@ -127,6 +127,41 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { } else { $modLoader.$log("[VFS_WEB] Starting HTTP server"); + function readRangeHeader(range, totalLength) { + /* + * Example of the method 'split' with regular expression. + * + * Input: bytes=100-200 + * Output: [null, 100, 200, null] + * + * Input: bytes=-200 + * Output: [null, null, 200, null] + */ + + if (range == null || range.length == 0) + return null; + + var array = range.split(/bytes=([0-9]*)-([0-9]*)/); + var start = parseInt(array[1]); + var end = parseInt(array[2]); + var result = { + Start: isNaN(start) ? 0 : start, + End: isNaN(end) ? (totalLength - 1) : end + }; + + if (!isNaN(start) && isNaN(end)) { + result.Start = start; + result.End = totalLength - 1; + } + + if (isNaN(start) && !isNaN(end)) { + result.Start = totalLength - end; + result.End = totalLength - 1; + } + + return result; + } + let SERVER_KEY = require('crypto').randomBytes(32).toString('hex'); let TEST_KEY = require('crypto').randomBytes(32).toString('hex'); let testKeyChecker; @@ -139,7 +174,7 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { res.setHeader("Content-Type", "application/json"); res.writeHead(200); res.end('{"ready": true}'); - + $modLoader.$vfsTrace(`[VFS_WEB] ${TEST_KEY}`); $modLoader.$log(`[VFS_WEB] Got Test key. Successfully authenticated client.`); @@ -154,11 +189,12 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { return; } - + let requestPath = req.url.split(new RegExp(`^/${SERVER_KEY}`))[1]; if (requestPath.startsWith("/www/")) { requestPath = requestPath.replace(/^\/www/, ""); } + res.setHeader("Accept-Ranges", "bytes"); try { const data = await _vfs_resolve_file(requestPath); @@ -169,11 +205,24 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { res.setHeader("Content-Type", type); res.setHeader("Access-Control-Allow-Origin", window.location.origin); - res.writeHead(200); - res.end(data); - + let rangeRequest = readRangeHeader(req.headers["range"], data.length); + if (rangeRequest == null) { + res.setHeader("Content-Length", data.length); + res.writeHead(200); + res.end(data); + } else { + res.setHeader("Content-Range", "bytes " + rangeRequest.Start + "-" + rangeRequest.End + "/" + data.length); + res.setHeader("Content-Length", ( + rangeRequest.End - rangeRequest.Start + 1 + )); + res.writeHead(206); + + res.end(data.subarray(rangeRequest.Start, rangeRequest.End + 1)); + } + $modLoader.$vfsTrace(`[VFS_WEB] Got request ${req.url} (${requestPath})`); } catch(E) { + console.log(E); res.writeHead(404); res.end(''); } From f6b72d21d3f6638bfa221dcb375de5023cccd326 Mon Sep 17 00:00:00 2001 From: nift4 Date: Sat, 15 Jun 2024 12:38:25 +0200 Subject: [PATCH 4/5] Support macOS port The official macOS port does not use a /www subfolder and the game's root directory (where latest.log is placed) is the same directory the index.html and JavaScript files are in. --- www/modloader/vfs_web.js | 113 ++++++++++++--------------------------- 1 file changed, 35 insertions(+), 78 deletions(-) diff --git a/www/modloader/vfs_web.js b/www/modloader/vfs_web.js index 22aefed..e48be04 100644 --- a/www/modloader/vfs_web.js +++ b/www/modloader/vfs_web.js @@ -4,59 +4,30 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { $modLoader.$vfsTrace("WEB REQUEST " + JSON.stringify(data)); let url = new URL(data.request.url); - if ($modLoader.isInTestMode) { - if (url.origin === window.location.origin) { - let vfsPath = url.pathname; - try { - let rdata = await _vfs_resolve_file(vfsPath); - let hS = ""; - for (let header in data.responseHeaders) { - hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; - } - - let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; - responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); - return { - interceptionId: data.interceptionId, - rawResponse: responseBody - }; - } catch(e) { - window._logLine("Error occured when building response body: " + e.stack); - return { - interceptionId: data.interceptionId - } + if (url.origin === window.location.origin) { + let vfsPath = url.pathname.replace(/^[\/\\]*www[\/\\]*/, ""); + try { + let rdata = await _vfs_resolve_file(vfsPath); + let hS = ""; + for (let header in data.responseHeaders) { + hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; } - } else { + + let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; + responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); + return { + interceptionId: data.interceptionId, + rawResponse: responseBody + }; + } catch(e) { + window._logLine("Error occured when building response body: " + e.stack); return { interceptionId: data.interceptionId } } } else { - if (url.origin === window.location.origin && url.pathname.startsWith("/www/")) { - let vfsPath = url.pathname.replace(/^[\/\\]*www[\/\\]*/, ""); - try { - let rdata = await _vfs_resolve_file(vfsPath); - let hS = ""; - for (let header in data.responseHeaders) { - hS = `${hS}${header}: ${data.responseHeaders[header]}\n`; - } - - let responseBody = `HTTP/1.1 200 OK\n${hS}\n`; - responseBody = Buffer.concat([Buffer.from(responseBody), rdata]).toString("base64"); - return { - interceptionId: data.interceptionId, - rawResponse: responseBody - }; - } catch(e) { - window._logLine("Error occured when building response body: " + e.stack); - return { - interceptionId: data.interceptionId - } - } - } else { - return { - interceptionId: data.interceptionId - } + return { + interceptionId: data.interceptionId } } } @@ -82,35 +53,19 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { chrome.debugger.sendCommand(debugee, "Network.continueInterceptedRequest", await buildResponseBody(data)); } }); - if (!$modLoader.isInTestMode) { - chrome.debugger.sendCommand( - debugee, - "Network.setRequestInterception", - { - enabled: true, - patterns: [ - { - urlPattern: window.location.origin + "/www/*", - interceptionStage: "HeadersReceived" - } - ] - } - ); - } else { - chrome.debugger.sendCommand( - debugee, - "Network.setRequestInterception", - { - enabled: true, - patterns: [ - { - urlPattern: window.location.origin + "/*", - interceptionStage: "HeadersReceived" - } - ] - } - ); - } + chrome.debugger.sendCommand( + debugee, + "Network.setRequestInterception", + { + enabled: true, + patterns: [ + { + urlPattern: window.location.origin + "/*", + interceptionStage: "HeadersReceived" + } + ] + } + ); setTimeout(resolve, 100); }); @@ -243,6 +198,7 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { $modLoader.$log(`[VFS_WEB] Proxy server listening on Port 127.0.0.1:${port}`); function beforeRequestInterceptor(details) { + $modLoader.$vfsTrace("VFS_WEB REQUEST " + JSON.stringify(details)); let u = new URL(details.url); if (u.pathname === "/www/modloader/one_loader_sw.js") return null; return {redirectUrl: `http://127.0.0.1:${port}/${SERVER_KEY}${u.pathname}`} @@ -250,12 +206,13 @@ function _modLoader_install_debugger_vfs(shadowfs, nativefs) { chrome.webRequest.onBeforeRequest.addListener(beforeRequestInterceptor, { urls: [ - window.location.origin + "/www/*", - window.location.origin + "/" + TEST_KEY + window.location.origin + "/*" ] }, ["blocking"]) + $modLoader.$log(`[VFS_WEB] Registered request listener ${window.location.origin}`); window.addEventListener("beforeunload", function(e) { + $modLoader.$log(`[VFS_WEB] Unregistering request listener ${window.location.origin}`); chrome.webRequest.onBeforeRequest.removeListener(beforeRequestInterceptor); server.close(); }); From dbd4764a3fcfe36ed9976834a37c3ff19e3d5e61 Mon Sep 17 00:00:00 2001 From: nift4 Date: Sun, 4 Aug 2024 19:44:54 +0200 Subject: [PATCH 5/5] support automatic updates on mac --- www/mods/oneloader/oneloader-update-installer.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/www/mods/oneloader/oneloader-update-installer.js b/www/mods/oneloader/oneloader-update-installer.js index 2c71a3e..631383d 100644 --- a/www/mods/oneloader/oneloader-update-installer.js +++ b/www/mods/oneloader/oneloader-update-installer.js @@ -14,10 +14,16 @@ if ($modLoader.config && $modLoader.config._autoUpdater && $modLoader.config._au fs.writeFileSync("_oneloader_update.zip", Buffer.from(bundle)); let zip = new StreamZip.async({ file: "_oneloader_update.zip" }); let entries = await zip.entries(); + let stripWww = false; + try { + fs.accessSync('www'); + } catch (E) { + stripWww = true; + } for (let el in entries) { - try { if (entries[el].isDirectory) { fs.mkdirSync(el); } } catch (e) { } + try { if (entries[el].isDirectory) { fs.mkdirSync(stripWww ? el.replace(/^[\/\\]*www[\/\\]*/, "") : el); } } catch (e) { } if (entries[el].isDirectory) continue; - fs.writeFileSync(el, await zip.entryData(el)); + fs.writeFileSync(stripWww ? el.replace(/^[\/\\]*www[\/\\]*/, "") : el, await zip.entryData(el)); } $modLoader.config._autoUpdater.performUpdate = false; $modLoader.config._autoUpdater.updateBundleURL = undefined; @@ -37,4 +43,4 @@ if ($modLoader.config && $modLoader.config._autoUpdater && $modLoader.config._au console.log(E); window._logLine("Failed to fetch or install update"); } -} \ No newline at end of file +}