diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 2aa7398..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "native/src/wmi"] - path = native/src/wmi - url = https://github.com/HolographicHat/wmi diff --git a/app.js b/app.js deleted file mode 100644 index 18deb9e..0000000 --- a/app.js +++ /dev/null @@ -1,179 +0,0 @@ -const proxy = require("udp-proxy") -const cp = require("child_process") -const appcenter = require("./appcenter") -const regionServer = require("./regionServer") -const cloud = require("./generated/secret") -const { - initConfig, splitPacket, upload, decodeProto, log, setupHost, KPacket, debug, checkUpdate, - brotliCompressSync, brotliDecompressSync, checkGameIsRunning, checkPortIsUsing -} = require("./utils") -const { exportData } = require("./export") -const { enablePrivilege, pause } = require("./generated/native") - -const onExit = () => { - setupHost(true) - console.log("按任意键退出") - pause() -}; - -(async () => { - try { - process.on("SIGHUP", () => setupHost(true)) - process.on("unhandledRejection", (reason, promise) => { - console.log("Unhandled Rejection at: ", promise, "\n0Reason:", reason) - }) - process.on("uncaughtException", (err, origin) => { - appcenter.uploadError(err, true) - console.log(err) - console.log(`Origin: ${origin}`) - process.exit(1) - }) - process.on("exit", onExit) - process.on("SIGINT", onExit) - try { - enablePrivilege() - } catch (e) { - console.log("请使用管理员身份运行此程序") - process.exit(-1) - } - appcenter.startup() - let conf = await initConfig() - cloud.init(conf) - checkPortIsUsing() - checkGameIsRunning() - log("检查更新") - await checkUpdate() - let gameProcess - let unexpectedExit = true - regionServer.create(conf,() => { - setupHost() - gameProcess = cp.execFile(conf.executable, { cwd: conf.path },err => { - if (err !== null && !err.killed) { - throw err - } - }) - log("启动原神") - gameProcess.on("exit", () => { - if (unexpectedExit) { - console.log("游戏进程异常退出") - process.exit(0) - } - }) - },(ip, port, hServer) => { - let login = false - let cache = new Map() - let lastRecvTimestamp = 0 - // noinspection JSUnusedGlobalSymbols - const options = { - address: ip, - port: port, - localaddress: "127.0.0.1", - localport: 45678, - middleware: { - message: (msg, sender, next) => { - const buf = Buffer.from(msg) - if (!(login && buf.readUInt8(8) === 0x51)) { - next(msg, sender) - } - }, - proxyMsg: (msg, sender, peer, next) => { - try { next(msg, sender, peer) } catch (e) {} - } - } - } - let monitor; - let stopped = false - const createMonitor = () => { - monitor = setInterval(async () => { - if (login && lastRecvTimestamp + 2 < parseInt(Date.now() / 1000) && !stopped) { - stopped = true - unexpectedExit = false - server.close(() => {}) - hServer.close() - gameProcess.kill() - clearInterval(monitor) - setupHost(true) - log("正在处理数据,请稍后...") - let packets = Array.from(cache.values()) - cache.clear() - packets.sort((a, b) => a.frg - b.frg) - .sort((a, b) => a.sn - b.sn) - .filter(i => i.data.byteLength !== 0) - .forEach(i => { - const psn = i.sn + i.frg - cache.has(psn) ? (() => { - const arr = cache.get(psn) - arr.push(i.data) - cache.set(psn, arr) - })() : cache.set(psn, [i.data]) - }) - packets = Array.from(cache.values()) - .map(arr => { - const data = Buffer.concat(arr) - const len = Buffer.alloc(4) - len.writeUInt32LE(data.length) - return Buffer.concat([len, data]) - }) - const merged = Buffer.concat(packets) - const compressed = brotliCompressSync(merged) - const response = await upload(compressed) - const data = brotliDecompressSync(response.data) - if (response.status !== 200) { - log(`发生错误: ${data}`) - log(`请求ID: ${response.headers["x-api-requestid"]}`) - log("请联系开发者以获取帮助") - } else { - const proto = await decodeProto(data, "Notify1") - await exportData(proto) - } - process.exit(0) - } - },1000) - } - const server = proxy.createServer(options) - server.on("message", (msg, _) => { - if (msg.byteLength > 500) { - login = true - } - }) - server.on("error", err => console.log(`Proxy error: ${err.message}` + err.message)) - server.on("proxyError", err => console.log(`Proxy error: ${err.message}` + err.message)) - server.on("proxyMsg", (msg, _) => { - lastRecvTimestamp = parseInt(Date.now() / 1000) - let buf = Buffer.from(msg) - if (buf.byteLength <= 20) { - switch(buf.readUInt32BE(0)) { - case 325: - createMonitor() - debug("Connection established.") - break - case 404: - debug("Connection terminated.") - lastRecvTimestamp = parseInt(Date.now() / 1000) - 2333 - break - default: - console.log(`Unhandled: ${buf.toString("hex")}`) - process.exit(2) - break - } - return - } - splitPacket(buf).forEach(sb => { - if (sb.readUInt8(8) === 0x51) { - const p = new KPacket(sb) - if (!cache.has(p.hash)) cache.set(p.hash, p) - } - }) - }) - return server - }).then(() => log("加载完毕")) - } catch (e) { - console.log(e) - if (e instanceof Error) { - appcenter.uploadError(e, true) - } else { - appcenter.uploadError(Error(e), true) - } - process.exit(1) - } -})() diff --git a/appcenter.js b/appcenter.js deleted file mode 100644 index 0b30430..0000000 --- a/appcenter.js +++ /dev/null @@ -1,116 +0,0 @@ -const axios = require("axios") -const crypto = require("crypto") -const { version } = require("./version") -const { getDeviceID, getDeviceInfo } = require("./generated/native") - -const getTimestamp = (d = new Date()) => { - const p = i => i.toString().padStart(2, "0") - return `${d.getUTCFullYear()}-${p(d.getUTCMonth() + 1)}-${p(d.getUTCDate())}T${p(d.getUTCHours())}:${p(d.getUTCMinutes())}:${p(d.getUTCSeconds())}.${p(d.getUTCMilliseconds())}Z` -} - -const queue = [] -const session = crypto.randomUUID() -const key = "648b83bf-d439-49bd-97f4-e1e506bdfe39" - -const install = (() => { - const id = getDeviceID() - return id === undefined ? crypto.randomUUID() : id -})() - -const device = (() => { - const info = getDeviceInfo() - info.appBuild = version.code - info.appVersion = version.name - info.sdkName = "appcenter.wpf.netcore" - info.sdkVersion = "4.5.0" - info.osName = "WINDOWS" - info.appNamespace = "default" - return info -})() - -const upload = () => { - if (queue.length > 0) { - const logs = [] - for (let i = 0; i <= queue.length; i++) { - logs.push(queue.pop()) - } - const data = JSON.stringify({"logs": logs}) - axios.post("https://in.appcenter.ms/logs?api-version=1.0.0", data,{ - headers: { - "App-Secret": key, - "Install-ID": install - } - }).catch(_ => {}).then() - } -} - -const uploadError = (err, fatal) => { - const eid = crypto.randomUUID() - const reportJson = process.report.getReport(err) - const reportAttachment = { - type: "errorAttachment", - device: device, - timestamp: getTimestamp(), - id: crypto.randomUUID(), - sid: session, - errorId: eid, - contentType: "application/json", - fileName: "report.json", - data: Buffer.from(JSON.stringify(reportJson, null, 2), "utf-8").toString("base64") - } - // noinspection JSUnresolvedVariable - const errorContent = { - type: "managedError", - id: eid, - sid: session, - architecture: "AMD64", - userId: install, - fatal: fatal, - processId: process.pid, - processName: process.argv0.replaceAll("\\", "/").split("/").pop(), - timestamp: getTimestamp(), - appLaunchTimestamp: getTimestamp(new Date(Date.now() - process.uptime())), - exception: { - "type": err.name, - "message": err.message, - "stackTrace": err.stack - }, - device: device - } - queue.push(errorContent, reportAttachment) - upload() -} - -const uploadEvent = (name, prop) => { - const content = { - type: "event", - id: crypto.randomUUID(), - sid: session, - name: name, - properties: prop, - timestamp: getTimestamp(), - device: device - } - queue.push(content) -} - -const startup = () => { - queue.push({ - type: "startService", - services: [ "Analytics","Crashes" ], - timestamp: getTimestamp(), - device: device - }) - queue.push({ - type: "startSession", - sid: session, - timestamp: getTimestamp(), - device: device - }) - upload() - setInterval(() => upload(), 5000) -} - -module.exports = { - startup, upload, uploadError, uploadEvent -} diff --git a/export.js b/export.js deleted file mode 100644 index 85ff727..0000000 --- a/export.js +++ /dev/null @@ -1,162 +0,0 @@ -const fs = require("fs") -const axios = require("axios") -const readline = require("readline") -const { version } = require("./version") -const { loadCache, log, openUrl } = require("./utils") -const { checkSnapFastcall, copyToClipboard } = require("./generated/native") - -const exportToSeelie = proto => { - const out = { achievements: {} } - proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id}) => { - out.achievements[id === 81222 ? 81219 : id] = { done: true } - }) - const fp = `./export-${Date.now()}-seelie.json` - fs.writeFileSync(fp, JSON.stringify(out)) - log(`导出为文件: ${fp}`) -} - -const exportToPaimon = async proto => { - const out = { achievement: {} } - const data = await loadCache() - proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id}) => { - const gid = data["a"][id]["g"] - if (out.achievement[gid] === undefined) { - out.achievement[gid] = {} - } - out.achievement[gid][id === 81222 ? 81219 : id] = true - }) - const fp = `./export-${Date.now()}-paimon.json` - fs.writeFileSync(fp, JSON.stringify(out)) - log(`导出为文件: ${fp}`) -} - -const UIAF = proto => { - const out = { - info: { - export_app: "YaeAchievement", - export_timestamp: Date.now(), - export_app_version: version.name, - uiaf_version: "v1.0" - }, - list: [] - } - proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id, finishTimestamp, current}) => { - out.list.push({ - id: id, - timestamp: finishTimestamp, - current: current - }) - }) - return out -} - -const exportToSnapGenshin = async proto => { - if (checkSnapFastcall()) { - const result = UIAF(proto) - const json = JSON.stringify(result) - copyToClipboard(json) - openUrl(`snapgenshin://achievement/import/uiaf`) - log("在 SnapGenshin 进行下一步操作") - } else { - log("请更新 SnapGenshin 后重试") - } -} - -const exportToCocogoat = async proto => { - const result = UIAF(proto) - const response = await axios.post(`https://77.cocogoat.work/v1/memo?source=${encodeURI("全部成就")}`, result).catch(_ => { - console.log("网络错误,请检查网络后重试 (26-1)") - process.exit(261) - }) - if (response.status !== 201) { - console.log(`API StatusCode 错误,请联系开发者以获取帮助 (26-2-${response.status})`) - process.exit(262) - } - const retcode = openUrl(`https://cocogoat.work/achievement?memo=${response.data.key}`) - if (retcode > 32) { - log("在浏览器内进行下一步操作") - } else { - log(`打开此链接以进行下一步操作: https://cocogoat.work/achievement?memo=${response.data.key}`) - } -} - -const exportToCsv = async proto => { - const data = await loadCache() - const outputLines = ["ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间"] - const getStatusText = i => { - switch (i) { - case 1: return "未完成" - case 2: return "已完成,未领取奖励" - case 3: return "已完成" - default: return "未知" - } - } - const getTime = ts => { - const d = new Date(parseInt(`${ts}000`)) - const p = i => i.toString().padStart(2, "0") - return `${d.getFullYear()}/${p(d.getMonth() + 1)}/${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}` - } - const bl = [84517] - proto.list.forEach(({current, finishTimestamp, id, status, require}) => { - if (!bl.includes(id)) { - const curAch = data["a"][id] === undefined ? (() => { - console.log(`Error get id ${id} in excel`) - return { - g: "未知", - n: "未知", - d: "未知" - } - })() : data["a"][id] - outputLines.push(`${id},${getStatusText(status)},${data["g"][curAch.g]},${curAch.n},${curAch.d},${status !== 1 ? current === 0 ? require : current : current},${require},${status === 1 ? "" : getTime(finishTimestamp)}`) - } - }) - const fp = `./export-${Date.now()}.csv` - fs.writeFileSync(fp, `\uFEFF${outputLines.join("\n")}`) - log(`导出为文件: ${fp}`) -} - -const exportData = async proto => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }) - const question = (query) => new Promise(resolve => { - rl.question(query, resolve) - }) - const chosen = await question( - [ - "导出至: ", - "[0] 椰羊 (https://cocogoat.work/achievement)", - "[1] SnapGenshin", - "[2] Paimon.moe", - "[3] Seelie.me", - "[4] 表格文件 (默认)", - "输入一个数字(0-4): " - ].join("\n") - ) - rl.close() - switch (chosen.trim()) { - case "0": - await exportToCocogoat(proto) - break - case "1": - await exportToSnapGenshin(proto) - break - case "2": - await exportToPaimon(proto) - break - case "3": - await exportToSeelie(proto) - break - case "raw": - fs.writeFileSync(`./export-${Date.now()}-raw.json`, JSON.stringify(proto,null,2)) - log("OK") - break - default: - await exportToCsv(proto) - } -} - -module.exports = { - exportData -} diff --git a/native/.gitignore b/native/.gitignore deleted file mode 100644 index 4978b9c..0000000 --- a/native/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -CMakeLists.txt -.idea -cmake-build-debug -node_modules -build -src/homu.cpp diff --git a/native/binding.gyp b/native/binding.gyp deleted file mode 100644 index 4faea00..0000000 --- a/native/binding.gyp +++ /dev/null @@ -1,48 +0,0 @@ -{ - "targets": [ - { - "target_name": "native", - "sources": [ - "src/main.cc", - "src/utils.h", - "src/utils.cc", - "src/define.h", - "src/wmi/wmi.cpp", - "src/wmi/wmi.hpp", - "src/wmi/unistd.h", - "src/VMProtectSDK.h", - "src/wmi/wmiresult.cpp", - "src/wmi/wmiresult.hpp", - "src/wmi/wmiclasses.hpp", - "src/wmi/wmiexception.hpp", - "src/registry/registry.hpp" - ], - "cflags!": [ - "-fno-exceptions" - ], - "cflags_cc!": [ - "-fno-exceptions" - ], - "defines": [ - "NAPI_DISABLE_CPP_EXCEPTIONS" - ], - "include_dirs": [ - " -#include -#include -#include -#include - -using std::string, std::wstring, std::cout, std::to_string, std::unique_ptr, std::make_unique; -using Napi::Object, Napi::Env, Napi::Function, Napi::Value, Napi::CallbackInfo, Napi::TypeError, Napi::Error; - -typedef unsigned char byte; -typedef unsigned int ui; -typedef unsigned long ul; -typedef unsigned long long ull; diff --git a/native/src/homu.h b/native/src/homu.h deleted file mode 100644 index d63e69f..0000000 --- a/native/src/homu.h +++ /dev/null @@ -1,11 +0,0 @@ -// -// Created by holog on 2022/5/20. -// -#include "define.h" - -Value Initialize(const CallbackInfo &info); - -#ifndef GENSHIN_EXPORT_NATIVE_HOMU_H -#define GENSHIN_EXPORT_NATIVE_HOMU_H - -#endif //GENSHIN_EXPORT_NATIVE_HOMU_H diff --git a/native/src/main.cc b/native/src/main.cc deleted file mode 100644 index 0ea3718..0000000 --- a/native/src/main.cc +++ /dev/null @@ -1,250 +0,0 @@ -//#include "homu.h" -#include "utils.h" -#include "define.h" -#include "wmi/wmi.hpp" -#include "wmi/wmiclasses.hpp" -#include "registry/registry.hpp" -#include -#include -#pragma comment(lib,"ws2_32.lib") -#pragma comment(lib,"iphlpapi.lib") - -using Wmi::Win32_ComputerSystem, Wmi::Win32_OperatingSystem, Wmi::retrieveWmi; - -namespace native { - - Value CheckGameIsRunning(const CallbackInfo &info) { - Env env = info.Env(); - if (info.Length() != 1 || !info[0].IsString()) { - TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - wstring name = StringToWString(info[0].As().Utf8Value()); - bool isRunning = false; - PROCESSENTRY32 entry; - entry.dwSize = sizeof(entry); - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - if (Process32First(snapshot, &entry) == TRUE) { - while (Process32Next(snapshot, &entry) == TRUE) { - if (wstring(entry.szExeFile) == name) { - isRunning = true; - } - } - } - CloseHandle(snapshot); - return Napi::Boolean::New(env, isRunning); - } - - Value SelectGameExecutable(const CallbackInfo &info) { - Env env = info.Env(); - Napi::String path; - if (OpenFile(env, path) != ERROR_SUCCESS) { - Error::New(env, "Failed to open file: " + to_string(CommDlgExtendedError())).ThrowAsJavaScriptException(); - return env.Undefined(); - } - return path; - } - - Value WhoUseThePort(const CallbackInfo &info) { - Env env = info.Env(); - if (info.Length() != 1 || !info[0].IsNumber()) { - TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); - return env.Undefined(); - } - DWORD dwSize = 0; - PMIB_TCPTABLE_OWNER_PID pTcpTable = nullptr; - GetExtendedTcpTable(pTcpTable, &dwSize, TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,0); - pTcpTable = (PMIB_TCPTABLE_OWNER_PID)new byte[dwSize]; - if(GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,0) != NO_ERROR) { - Error::New(env, "GetExtendedTcpTable failed").ThrowAsJavaScriptException(); - return env.Undefined(); - } - int port = info[0].As().Int32Value(); - auto nNum = (int)pTcpTable->dwNumEntries; - DWORD pid = 0; - for(int i = 0; i < nNum; i++) { - if (htons(pTcpTable->table[i].dwLocalPort) == port) { - pid = pTcpTable->table[i].dwOwningPid; - break; - } - } - delete pTcpTable; - Value ret = env.Undefined(); - if (pid != 0) { - HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); - if (hProcess == nullptr) { - Error::New(env, "OpenProcess error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); - return env.Undefined(); - } - TCHAR fnBuf[MAX_PATH]; - DWORD length = MAX_PATH; - if (QueryFullProcessImageName(hProcess, 0, fnBuf, &length) == 0) { - Error::New(env, "QueryFullProcessImageName error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); - return env.Undefined(); - } - Object obj = Object::New(env); - obj.Set("pid", Napi::Number::New(env, pid)); - obj.Set("path", Napi::String::New(env, WStringToString(fnBuf))); - ret = obj; - } - return ret; - } - - Value GetDeviceID(const CallbackInfo &info) { - Env env = info.Env(); - wstring wd; - if (RegUtils::GetString(HKEY_CURRENT_USER, L"SOFTWARE\\miHoYoSDK", L"MIHOYOSDK_DEVICE_ID", wd) != ERROR_SUCCESS) { - return env.Undefined(); - } - string id = WStringToString(wd); - return Napi::String::New(env, id.substr(0, 8) + "-" + id.substr(8, 4) + "-" + id.substr(12, 4) + "-" + id.substr(16, 4) + "-" + id.substr(20, 12)); - } - - Value GetDeviceInfo(const CallbackInfo &info) { - Env env = info.Env(); - HDC desktop = GetDC(nullptr); - int sw = GetDeviceCaps(desktop, DESKTOPHORZRES); - int sh = GetDeviceCaps(desktop, DESKTOPVERTRES); - ReleaseDC(nullptr, desktop); - DWORD buildNum = RegUtils::GetInt(env, HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"UBR", 0); - wstring locale; - if (RegUtils::GetString(HKEY_CURRENT_USER, L"Control Panel\\International", L"LocaleName", locale) != ERROR_SUCCESS) { - locale = L"zh-CN"; - } - wstring country; - if (RegUtils::GetString(HKEY_CURRENT_USER, L"Control Panel\\International\\Geo", L"Name", country) != ERROR_SUCCESS) { - country = L"CN"; - } - Object obj = Object::New(env); - obj.Set("locale", Napi::String::New(env, WStringToString(locale))); - obj.Set("screenSize", Napi::String::New(env, to_string(sw) + "x" + to_string(sh))); - obj.Set("carrierCountry", Napi::String::New(env, WStringToString(country))); - try { - auto computer = retrieveWmi(); - obj.Set("model", Napi::String::New(env, computer.Model)); - obj.Set("oemName", Napi::String::New(env, computer.Manufacturer)); - } catch (Wmi::WmiException &e) { - obj.Set("model", Napi::String::New(env, "Unknown")); - obj.Set("oemName", Napi::String::New(env, "Unknown")); - } - try { - auto os = retrieveWmi(); - string osv = os.Version; - obj.Set("osBuild", Napi::String::New(env, osv + "." + to_string(buildNum))); - obj.Set("osVersion", Napi::String::New(env, osv)); - obj.Set("timeZoneOffset", Napi::Number::New(env, os.CurrentTimeZone)); - } catch (Wmi::WmiException &e) { - obj.Set("osBuild", Napi::String::New(env, "Unknown")); - obj.Set("osVersion", Napi::String::New(env, "Unknown")); - obj.Set("timeZoneOffset", Napi::Number::New(env, 480)); - } - return obj; - } - - Value EnablePrivilege(const CallbackInfo &info) { - Env env = info.Env(); - HANDLE hToken; - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { - Error::New(env, "OpenProcessToken error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); - return env.Undefined(); - } - TOKEN_PRIVILEGES tp; - ZeroMemory(&tp, sizeof(tp)); - tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!LookupPrivilegeValue(nullptr, L"SeDebugPrivilege", &tp.Privileges[0].Luid)) { - Error::New(env, "LookupPrivilegeValue error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); - return env.Undefined(); - } - if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, nullptr)) { - Error::New(env, "AdjustTokenPrivileges error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); - return env.Undefined(); - } - if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { - Error::New(env, "The token does not have the specified privilege.").ThrowAsJavaScriptException(); - return env.Undefined(); - } - CloseHandle(hToken); - return env.Undefined(); - } - - Value CopyToClipboard(const CallbackInfo &info) { - Env env = info.Env(); - string text = info[0].As().Utf8Value(); - if (OpenClipboard(nullptr)) { - EmptyClipboard(); - HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, text.length() + 1); - if (hg != nullptr) { - memcpy(GlobalLock(hg), text.c_str(), text.length() + 1); - GlobalUnlock(hg); - SetClipboardData(CF_TEXT, hg); - } - CloseClipboard(); - } - return env.Undefined(); - } - - Value Pause(const CallbackInfo &info) { - while(!_kbhit()) { - Sleep(10); - } - return info.Env().Undefined(); - } - - Value OpenUrl(const CallbackInfo &info) { - Env env = info.Env(); - wstring url = StringToWString(info[0].As().Utf8Value()); - HINSTANCE retcode = ShellExecute(GetConsoleWindow(), L"open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL); - return Napi::Number::New(env, (INT_PTR)retcode); // NOLINT(cppcoreguidelines-narrowing-conversions) - } - - Value CheckSnapFastcall(const CallbackInfo &info) { - Env env = info.Env(); - wstring queryResult; - RegUtils::GetString(HKEY_CLASSES_ROOT, L"snapgenshin", L"", queryResult); - return Napi::Boolean::New(env, wcscmp(queryResult.c_str(), L"URL:snapgenshin") == 0); - } - - Value GetMACAddress(const CallbackInfo &info) { - Env env = info.Env(); - ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO); - auto pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); - if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) { - pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); - if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) { return env.Undefined(); } - } - PIP_ADAPTER_INFO pAdapter = pAdapterInfo; - while (pAdapter) { - if (pAdapter->Address[0] == 0x00) { - pAdapter = pAdapter->Next; - continue; - } - char *result; - ToHex((char *)pAdapter->Address, 6, &result); - auto ret = Napi::String::New(env, result); - free(result); - free(pAdapterInfo); - return ret; - } - return env.Undefined(); - } - - Object init(Env env, Object exports) { - exports.Set("pause", Function::New(env, Pause)); - exports.Set("openUrl", Function::New(env, OpenUrl)); - //exports.Set("homuInit", Function::New(env, Initialize)); - exports.Set("getDeviceID", Function::New(env, GetDeviceID)); - exports.Set("getMACAddress", Function::New(env, GetMACAddress)); - exports.Set("getDeviceInfo", Function::New(env, GetDeviceInfo)); - exports.Set("whoUseThePort", Function::New(env, WhoUseThePort)); - exports.Set("copyToClipboard", Function::New(env, CopyToClipboard)); - exports.Set("enablePrivilege", Function::New(env, EnablePrivilege)); - exports.Set("checkSnapFastcall", Function::New(env, CheckSnapFastcall)); - exports.Set("checkGameIsRunning", Function::New(env, CheckGameIsRunning)); - exports.Set("selectGameExecutable", Function::New(env, SelectGameExecutable)); - return exports; - } - - NODE_API_MODULE(addon, init) - -} diff --git a/native/src/registry/registry.hpp b/native/src/registry/registry.hpp deleted file mode 100644 index 0a591fc..0000000 --- a/native/src/registry/registry.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#ifndef REGISTRY_HPP -#define REGISTRY_HPP - -#include "../define.h" - -namespace RegUtils { - - DWORD GetInt(Env env, HKEY hKey, const wstring &path, const wstring &value, DWORD defaultValue) { - DWORD data = 0; - DWORD size = sizeof(DWORD); - LSTATUS retcode = RegGetValue(hKey, path.c_str(), value.c_str(), RRF_RT_REG_DWORD, nullptr, &data, &size); - if (retcode != ERROR_SUCCESS) { - return defaultValue; - } - return data; - } - - LSTATUS GetString(HKEY hKey, const wstring &path, const wstring &value, wstring &result) { - DWORD size = 0; - LSTATUS retcode = RegGetValue(hKey, path.c_str(), value.c_str(), RRF_RT_REG_SZ, nullptr, nullptr, &size); - if (retcode != ERROR_SUCCESS) { - return retcode; - } - wstring data; - DWORD len = size / sizeof(WCHAR); - data.resize(len); - retcode = RegGetValue(hKey, path.c_str(), value.c_str(), RRF_RT_REG_SZ, nullptr, &data[0], &size); - if (retcode != ERROR_SUCCESS) { - return retcode; - } - data.resize((len-1)); - result = data; - return ERROR_SUCCESS; - } -} - -#endif //REGISTRY_HPP diff --git a/native/src/utils.cc b/native/src/utils.cc deleted file mode 100644 index f35a464..0000000 --- a/native/src/utils.cc +++ /dev/null @@ -1,62 +0,0 @@ -#include "utils.h" -#include "define.h" - -wstring StringToWString(const string &src, UINT codePage) { - int len = MultiByteToWideChar(codePage, 0, src.c_str(), -1, nullptr, 0); - auto *buffer = new WCHAR[len]; - MultiByteToWideChar(codePage, 0, src.c_str(), -1, buffer, len); - wstring strTemp(buffer); - delete[] buffer; - return strTemp; -} - -string WStringToString(const wstring &src, UINT codePage) { - int len = WideCharToMultiByte(codePage, 0, src.c_str(), -1, nullptr, 0, nullptr, nullptr); - auto *buffer = new CHAR[len]; - WideCharToMultiByte(codePage, 0, src.c_str(), -1, buffer, len, nullptr, nullptr); - string strTemp(buffer); - delete[] buffer; - return strTemp; -} - -void Log(Env env, const string &msg) { - auto logFunc = env.Global().Get("console").As().Get("log").As(); - logFunc.Call({ Napi::String::New(env, msg) }); -} - -void Log(Env env, const wstring &msg) { - auto logFunc = env.Global().Get("console").As().Get("log").As(); - logFunc.Call({ Napi::String::New(env, WStringToString(msg)) }); -} - -char* ToHex(const char *bin, int binsz, char **result) { - char hexStr[] = "0123456789ABCDEF"; - if (!(*result = (char *) malloc(binsz * 2 + 1))) return nullptr; - (*result)[binsz * 2] = 0; - if (!binsz) return nullptr; - for (int i = 0; i < binsz; i++) { - (*result)[i * 2 + 0] = hexStr[(bin[i] >> 4) & 0x0F]; - (*result)[i * 2 + 1] = hexStr[(bin[i]) & 0x0F]; - } - return *result; -} - -LSTATUS OpenFile(Env env, Napi::String &result, HWND parent) { - OPENFILENAME open; - ZeroMemory(&open, sizeof(open)); - WCHAR file[32768]; - file[0]=L'\0'; - open.Flags = OFN_FILEMUSTEXIST | OFN_NONETWORKBUTTON | OFN_PATHMUSTEXIST | OFN_EXPLORER; - open.hwndOwner = parent; - open.nMaxFile = 32768; - open.lpstrFile = file; - open.lpstrTitle = L"选择原神主程序"; - open.lpstrFilter = L"国服/国际服主程序 (YuanShen/GenshinImpact.exe)\0YuanShen.exe;GenshinImpact.exe\0"; - open.lStructSize = sizeof(open); - if(GetOpenFileName(&open)) { - result = GetACP() == 936 ? Napi::String::New(env, WStringToString(file, CP_UTF8)) : Napi::String::New(env, WStringToString(file)); - return ERROR_SUCCESS; - } else { - return ERROR_ERRORS_ENCOUNTERED; - } -} diff --git a/native/src/utils.h b/native/src/utils.h deleted file mode 100644 index c9c4a48..0000000 --- a/native/src/utils.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "define.h" - -string WStringToString(const wstring &src, UINT codePage = CP_ACP); -wstring StringToWString(const string &src, UINT codePage = CP_ACP); -LSTATUS OpenFile(Env env, Napi::String &result, HWND parent = GetConsoleWindow()); -char* ToHex(const char *bin, int binsz, char **result); -void Log(Env env, const string &msg); -void Log(Env env, const wstring &msg); - -#ifndef GENSHIN_EXPORT_NATIVE_UTILS_H -#define GENSHIN_EXPORT_NATIVE_UTILS_H - -#endif //GENSHIN_EXPORT_NATIVE_UTILS_H diff --git a/native/src/wmi b/native/src/wmi deleted file mode 160000 index aab36ea..0000000 --- a/native/src/wmi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit aab36ea1e1700f3d33f9dfc9f8df950c30e2c6ec diff --git a/package.json b/package.json deleted file mode 100644 index 0833deb..0000000 --- a/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "genshin-export", - "version": "1.0.0", - "description": "", - "main": "app.js", - "keywords": [], - "author": "", - "license": "ISC", - "private": true, - "dependencies": { - "axios": "^0.26.1", - "ini": "^2.0.0", - "protobufjs": "^6.11.2", - "udp-proxy": "^1.2.0" - }, - "devDependencies": { - "pkg": "file:C:/Users/holog/Desktop/pkg-main" - } -} diff --git a/package/app.manifest b/package/app.manifest deleted file mode 100644 index ea57069..0000000 --- a/package/app.manifest +++ /dev/null @@ -1,2 +0,0 @@ - -truetrue diff --git a/package/icon.ico b/package/icon.ico deleted file mode 100644 index d845ecc..0000000 Binary files a/package/icon.ico and /dev/null differ diff --git a/package/package.js b/package/package.js deleted file mode 100644 index 9e0f7ee..0000000 --- a/package/package.js +++ /dev/null @@ -1,57 +0,0 @@ -const fs = require("fs") -const pkg = require("pkg") -const { log } = require("../utils") -const { version } = require("../version") -const { execSync } = require("child_process") - -const name = `YaeAchievement-${ version.isDev ? "DevBuild" : "Release" }-${((t = new Date()) => { - const p = i => i.toString().padStart(2, "0") - return `${p(t.getMonth()+1)}${p(t.getDate())}${p(t.getHours())}${p(t.getMinutes())}` -})()}` - -const generateAndCompileVersionInfo = () => { - const vci = version.name.split(".") - let content = fs.readFileSync("./version.rc", "utf-8") - content = content.replaceAll("${BID}" , vci[2] === undefined ? "0" : vci[2]) - content = content.replaceAll("${YEAR}" , (new Date()).getFullYear()) - content = content.replaceAll("${MAJOR_VERSION}", vci[0]) - content = content.replaceAll("${MINOR_VERSION}", vci[1]) - content = content.replaceAll("${ORIGINAL_NAME}", `${name}.exe`) - fs.writeFileSync("./tmp.rc", content, "utf-8") - execSync(`rh.exe -open tmp.rc -save tmp.res -action compile -log NUL`) - fs.rm("./tmp.rc", () => {}) -} - -const generateScript = (path) => { - let content = fs.readFileSync("./rh.script", "utf-8") - content = content.replaceAll("${PATH}", path) - fs.writeFileSync("./tmp.script", content, "utf-8") -} - -const cleanUp = () => { - fs.rm("./rh.ini", () => {}) - fs.rm("./tmp.res", () => {}) - fs.rm("./tmp.script", () => {}) -} - - -const c = { - forWin7: false, - sevenZipPath: "\"C:/Program Files/7-Zip/7z.exe\"" -}; - -(async () => { - const fn = c.forWin7 ? `${name}-Win7` : name - const nv = c.forWin7 ? "14.19.1" : "16.14.2" - log("Generate and compile version info") - generateAndCompileVersionInfo() - log("Modify executable file resources") - const path = `C:/Users/holog/.pkg-cache/v3.3/built-v${nv}-win-x64` - generateScript(path) - execSync(`rh.exe -script tmp.script`) - cleanUp() - log("Build and compress package") - await pkg.exec(`../app.js -t node${nv.split(".")[0]}-win-x64 -C Brotli --build -o ${fn}.exe`.split(" ")) - execSync(`${c.sevenZipPath} a ${fn}.7z ${fn}.exe -mx=9 -myx=9 -mmt=4 -sdel -stl`) - log("Done") -})() diff --git a/package/rh.exe b/package/rh.exe deleted file mode 100644 index f224708..0000000 Binary files a/package/rh.exe and /dev/null differ diff --git a/package/rh.script b/package/rh.script deleted file mode 100644 index fa3f92b..0000000 --- a/package/rh.script +++ /dev/null @@ -1,10 +0,0 @@ -[FILENAMES] -Exe=${PATH} -SaveAs=${PATH} -Log=NUL -[COMMANDS] --delete MANIFEST,, --delete ICONGROUP,, --addoverwrite icon.ico, ICONGROUP,1,0 --addoverwrite tmp.res, VERSIONINFO,1,0 --addoverwrite app.manifest MANIFEST,1,0 diff --git a/package/version.rc b/package/version.rc deleted file mode 100644 index 54a6cf1..0000000 --- a/package/version.rc +++ /dev/null @@ -1,23 +0,0 @@ -1 VERSIONINFO -FILEVERSION ${MAJOR_VERSION},${MINOR_VERSION},${BID},0 -PRODUCTVERSION ${MAJOR_VERSION},${MINOR_VERSION},${BID},0 -FILEOS 0x4 -FILETYPE 0x1 -{ -BLOCK "StringFileInfo" -{ - BLOCK "000004B0" - { - VALUE "ProductName", "YaeAchievement" - VALUE "ProductVersion", "${MAJOR_VERSION}.${MINOR_VERSION}.${BID}" - VALUE "FileDescription", "YaeAchievement" - VALUE "FileVersion", "${MAJOR_VERSION}.${MINOR_VERSION}.${BID}" - VALUE "OriginalFilename", "${ORIGINAL_NAME}" - VALUE "LegalCopyright", "Copyright (c) ${YEAR} HolographicHat" - } -} -BLOCK "VarFileInfo" -{ - VALUE "Translation", 0x0000 0x04B0 -} -} diff --git a/regionServer.js b/regionServer.js deleted file mode 100644 index 362f0fe..0000000 --- a/regionServer.js +++ /dev/null @@ -1,111 +0,0 @@ -const fs = require("fs") -const https = require("https") -const axios = require("axios") -const { decodeProto, encodeProto, debug } = require("./utils") -const path = require("path") -const cert = path.join(__dirname, "./cache/cert/root.p12") - -const preparedRegions = {} -let currentProxy = undefined - -const getModifiedRegionList = async (conf) => { - const d = await axios.get(`https://${conf.dispatchUrl}/query_region_list`, { - responseType: "text", - params: { - version: conf.version, - channel_id: conf.channel, - sub_channel_id: conf.subChannel - } - }).catch(_ => { - console.log("网络错误,请检查网络后重试 (22-1)") - process.exit(221) - }) - const regions = await decodeProto(Buffer.from(d.data,"base64"),"QueryRegionList") - if (regions["retcode"] !== 0) { - console.log(`系统错误,请稍后重试 (${regions["retcode"]}-23)`) - process.exit(23) - } - regions.list = regions.list.map(item => { - const host = new URL(item.url).host - if (regions.list.length === 1) { - preparedRegions[host] = true - } - item.url = `https://localdispatch.yuanshen.com/query_cur_region/${host}` - return item - }) - return (await encodeProto(regions,"QueryRegionList")).toString("base64") -} - -const getModifiedRegionInfo = async (url, uc, hs) => { - const splitUrl = url.split("?") - const host = splitUrl[0].split("/")[2] - const noQueryRequest = splitUrl[1] === undefined - const query = noQueryRequest ? "" : `?${splitUrl[1]}` - const d = await axios.get(`https://${host}/query_cur_region${query}`, { - responseType: "text" - }).catch(_ => { - console.log("网络错误,请检查网络后重试 (22-2)") - process.exit(222) - }) - if (noQueryRequest) { - preparedRegions[host] = true - return d.data - } else { - const region = await decodeProto(Buffer.from(d.data,"base64"),"QueryCurRegion") - if (region["retcode"] !== 0) { - console.log(`${region["message"]} (${region["retcode"]}-24)`) - process.exit(24) - } - const info = region.info - if (preparedRegions[host]) { - if (currentProxy !== undefined) { - currentProxy.close() - } - debug("Create udp proxy: %s:%d", info.ip, info.port) - currentProxy = uc(info.ip, info.port, hs) - } else { - preparedRegions[host] = true - } - info.ip = "127.0.0.1" - info.port = 45678 - return (await encodeProto(region,"QueryCurRegion")).toString("base64") - } -} - -const agent = new https.Agent({ - rejectUnauthorized: false -}) - -const create = async (conf, regionListLoadedCallback, regionSelectCallback) => { - const regions = await getModifiedRegionList(conf) - const hServer = https.createServer({ - pfx: fs.readFileSync(cert), - passphrase: "" - }, async (request, response) => { - const url = request.url - debug("HTTP: %s", url) - response.writeHead(200, { "Content-Type": "text/html" }) - if (url.startsWith("/query_region_list")) { - response.end(regions) - } else if (url.startsWith("/query_cur_region")) { - const regionInfo = await getModifiedRegionInfo(url, regionSelectCallback, hServer) - response.end(regionInfo) - } else { - const frontResponse = await axios.get(`https://${conf.dispatchIP}${url}`, { - responseType: "arraybuffer", - httpsAgent: agent - }).catch(err => { - console.log("网络错误,请检查网络后重试 (22-3)") - console.log(err.message) - process.exit(223) - }) - response.end(frontResponse.data) - } - }).listen(443, "127.0.0.1", () => { - regionListLoadedCallback() - }) -} - -module.exports = { - create -} diff --git a/utils.js b/utils.js deleted file mode 100644 index 267f892..0000000 --- a/utils.js +++ /dev/null @@ -1,299 +0,0 @@ -const fs = require("fs") -const dns = require("dns") -const ini = require("ini") -const zlib = require("zlib") -const cloud = require("./generated/secret") -const native = require("./generated/native") -const readline = require("readline") -const protobuf = require("protobufjs") -const { version } = require("./version") -const { promisify } = require("util") -const { createHash } = require("crypto") -const path = require("path") -const { uploadEvent } = require("./appcenter") - -const messages = protobuf.loadSync(path.join(__dirname, "./generated/Messages.proto")) - -const encodeProto = (object, name) => { - const msgType = messages.lookupType(name) - const msgInst = msgType.create(object) - return msgType.encode(msgInst).finish() -} - -const decodeProto = (buf, name) => messages.lookupType(name).decode(buf) - -const checkPath = path => new Promise((resolve, reject) => { - if (!fs.existsSync(`${path}/UnityPlayer.dll`) || !fs.existsSync(`${path}/pkg_version`)) { - reject(`路径有误 ${path}`) - } else { - resolve(path) - } -}) - -let conf - -const initConfig = async () => { - const configFileName = "./config.json" - if (fs.existsSync(configFileName)) { - conf = JSON.parse(fs.readFileSync(configFileName, "utf-8")) - if (conf.offlineResource !== undefined || conf.customCDN !== undefined || conf.proxy !== undefined) { - conf.proxy = undefined - conf.customCDN = undefined - conf.offlineResource = undefined - fs.writeFileSync(configFileName, JSON.stringify(conf, null, 2)) - } - } else { - conf = { - path: [], - oversea_api: false - } - let p = "" - try { - p = path.dirname(native.selectGameExecutable()) - } catch (e) { - process.exit(1) - } - await checkPath(p).catch(reason => { - console.log(reason) - process.exit(1) - }) - conf.path.push(p) - fs.writeFileSync(configFileName, JSON.stringify(conf, null, 2)) - } - if (conf.oversea_api === undefined) { - conf.oversea_api = false - fs.writeFileSync(configFileName, JSON.stringify(conf, null, 2)) - } - if (conf.path.length === 1) { - await checkPath(conf.path[0]).catch(reason => { - console.log(reason) - process.exit(1) - }).then(p => { - conf.path = p - }) - } else { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }) - const question = (query) => new Promise(resolve => { - rl.question(query, resolve) - }) - const idx = await question(`选择客户端: \n${conf.path.map((s, i) => { - const fp = fs.existsSync(`${s}/GenshinImpact.exe`) ? `${s}\\GenshinImpact.exe` : `${s}\\YuanShen.exe` - return `[${i}] ${fp}` - }).join("\n")}\n> `) - await checkPath(conf.path[parseInt(idx)]).catch(reason => { - console.log(reason) - process.exit(1) - }).then(p => { - conf.path = p - }) - rl.close() - } - conf.isOversea = fs.existsSync(conf.path + "/GenshinImpact.exe") - conf.dataDir = conf.isOversea ? conf.path + "/GenshinImpact_Data" : conf.path + "/YuanShen_Data" - const readGameRes = (path) => fs.readFileSync(conf.dataDir + path, "utf-8") - const genshinConf = ini.parse(fs.readFileSync(`${conf.path}/config.ini`, "utf-8"))["General"] - conf.channel = genshinConf["channel"] - conf.subChannel = genshinConf["sub_channel"] - conf.version = readGameRes("/Persistent/ChannelName") + readGameRes("/Persistent/ScriptVersion") - conf.executable = conf.isOversea ? `${conf.path}/GenshinImpact.exe` : `${conf.path}/YuanShen.exe` - conf.dispatchUrl = `dispatch${conf.isOversea ? "os" : "cn"}global.yuanshen.com` - conf.dispatchIP = (await promisify(dns.lookup).bind(dns)(conf.dispatchUrl, 4)).address - uploadEvent("AppInitialize", { ClientVersion: version.name, GameVersion: conf.version }) - return conf -} - -const checkGameIsRunning = () => { - const name = path.basename(conf.executable) - if (native.checkGameIsRunning(name)) { - console.log("原神正在运行,请关闭后重试") - process.exit(301) - } -} - -const checkPortIsUsing = () => { - const portUsage = native.whoUseThePort(443) - if (portUsage !== undefined) { - console.log(`443端口被 ${path.basename(portUsage["path"])}(${portUsage["pid"]}) 占用,结束该进程后重试`) - process.exit(14) - } -} - -const openUrl = url => native.openUrl(encodeURI(url)) - -const splitPacket = buf => { - let offset = 0 - let arr = [] - while (offset < buf.length) { - let dataLength = buf.readUInt32LE(offset + 24) - arr.push(buf.subarray(offset, offset + 28 + dataLength)) - offset += dataLength + 28 - } - return arr -} - -const md5 = str => { - const h = createHash("md5") - h.update(str) - return h.digest("hex") -} - -/* - * Structure: - * UInt16 = 0x4321: magic num - * UInt8 = 0x2 : version - * Char[32] : data md5 - * ... : compressed data - */ - -const loadCache = async (fp = "latest-data") => { - log(`正在加载资源: ${fp}`) - const startAt = Date.now() - fs.mkdirSync("./cache", { recursive: true }) - // remove old cache file - fs.readdir("./cache/", (err, files) => { - if (err === null) files.forEach(s => { - const fn = `./cache/${s}` - if (!fn.endsWith(".ch")) fs.rm(fn,_ => {}) - }) - }) - const localPath = `./cache/${md5(fp)}.ch` - const headers = { - "x-content-hash": "0".repeat(32) - } - let fd = Buffer.alloc(0) - if (fs.existsSync(localPath)) { - fd = fs.readFileSync(localPath) - if (fd.readUInt16BE(0) === 0x4321 && fd.readUInt8(2) === 2) { - headers["x-content-hash"] = fd.subarray(3, 35).toString("utf-8") - } - } - const resp = await cloud.get(`/${fp}`, "application/json", headers, "arraybuffer") - if (resp.status === 304) { - log(`完成 (Cache, ${Date.now() - startAt}ms)`) - return JSON.parse(brotliDecompressSync(fd.subarray(35)).toString()) - } else { - const compressedData = resp.data - const decompressedData = brotliDecompressSync(compressedData) - const buf = Buffer.allocUnsafe(compressedData.length + 35) - buf.writeUInt16BE(0x4321, 0) - buf.writeUInt8(0x2, 2) - buf.fill(md5(decompressedData), 3) - buf.fill(compressedData, 35) - fs.writeFileSync(localPath, buf) - log(`完成 (Network, ${Date.now() - startAt}ms)`) - return JSON.parse(decompressedData) - } -} - -const isDebug = false - -const debug = (msg, ...params) => { - if (isDebug) log(msg, ...params) -} - -const log = (msg, ...params) => { - const time = new Date() - const timeStr = time.getHours().toString().padStart(2, "0") + ":" + time.getMinutes().toString().padStart(2, "0") + ":" + time.getSeconds().toString().padStart(2, "0") - console.log(`${timeStr} ${msg}`, ...params) -} - -const upload = async data => { - return await cloud.post("/achievement-export", data) -} - -const checkUpdate = async () => { - const data = (await cloud.fetchBucket("/latest.json")).data - if (data["vc"] !== version.code) { - console.log(`有可用更新: ${version.name} => ${data["vn"]}`) - console.log(`更新内容: \n${data["ds"]}`) - console.log("下载地址: https://github.com/HolographicHat/YaeAchievement/releases") - if (data["fc"] === true) { - console.log(" * 这是一次强制更新") - process.exit(410) - } - } -} - -const brotliCompressSync = data => zlib.brotliCompressSync(data,{ - params: { - [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY, - [zlib.constants.BROTLI_PARAM_SIZE_HINT]: data.length - } -}) - -const brotliDecompressSync = data => zlib.brotliDecompressSync(data) - -let hostsContent = undefined - -const setupHost = (restore = false) => { - if (restore && hostsContent === undefined) return - const path = `${process.env.windir}\\System32\\drivers\\etc\\hosts` - if (!fs.existsSync(path)) { - fs.writeFileSync(path, "") - } - fs.chmodSync(path, 0o777) - if (restore) { - fs.writeFileSync(path, hostsContent) - } else { - hostsContent = fs.readFileSync(path, "utf-8") - const requireHosts = new Map() - requireHosts.set(conf.dispatchUrl, "127.0.0.1") - requireHosts.set("localdispatch.yuanshen.com", "127.0.0.1") - const currentHosts = new Map() - hostsContent.split("\n").map(l => l.trim()).filter(l => !l.startsWith("#") && l.length > 0).forEach(value => { - const pair = value.trim().split(" ").filter(v => v.trim().length !== 0) - currentHosts.set(pair[1], pair[0]) - }) - requireHosts.forEach((value, key) => { - if (currentHosts.has(key)) { - if (currentHosts.get(key) === value) { - requireHosts.delete(key) - } else { - currentHosts.delete(key) - } - } - }) - requireHosts.forEach((ip, host) => { - currentHosts.set(host, ip) - }) - const newContent = Array.from(currentHosts.entries()).map(pair => { - return `${pair[1]} ${pair[0]}` - }).join("\n") - fs.writeFileSync(path, newContent) - } - debug("修改SystemHosts") - process.on("exit", () => { - fs.writeFileSync(path, hostsContent) - }) -} - -// noinspection JSUnusedGlobalSymbols -class KPacket { - - constructor(data) { - this.origin = data - this.conv = data.readUInt32BE(0) - this.token = data.readUInt32BE(4) - this.cmd = data.readUInt8(8) - this.frg = data.readUInt8(9) - this.wnd = data.readUInt16LE(10) - this.ts = data.readUInt32LE(12) - this.sn = data.readUInt32LE(16) - this.una = data.readUInt32LE(20) - this.length = data.readUInt32LE(24) - this.data = data.subarray(28) - this.hash = (() => { - const h = createHash("sha256") - h.update(Buffer.concat([Buffer.of(this.sn, this.frg), this.data])) - return h.digest("hex") - })() - } -} - -module.exports = { - log, encodeProto, decodeProto, initConfig, splitPacket, upload, brotliCompressSync, brotliDecompressSync, - setupHost, loadCache, debug, checkUpdate, KPacket, checkGameIsRunning, checkPortIsUsing, openUrl -} diff --git a/version.js b/version.js deleted file mode 100644 index 7d46a5e..0000000 --- a/version.js +++ /dev/null @@ -1,7 +0,0 @@ -const version = { - code: 8, - name: "1.7.0", - isDev: false -} - -module.exports = { version }