mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-13 18:08:15 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b76e8e3cd2 | ||
|
|
c12d376e21 | ||
|
|
d285b1c999 | ||
|
|
89ab4408d6 | ||
|
|
bf01214971 | ||
|
|
1900937a50 | ||
|
|
98a03a0910 | ||
|
|
82ba6120e4 | ||
|
|
6cb07d274a | ||
|
|
4acaa516bb | ||
|
|
b5caba2f7a | ||
|
|
ebb11bde9c |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,12 +1,6 @@
|
|||||||
cache
|
cache
|
||||||
cert
|
|
||||||
config.json
|
|
||||||
out.*
|
|
||||||
node_modules
|
|
||||||
.idea
|
.idea
|
||||||
YaeAchievement-*.7z
|
generated
|
||||||
YaeAchievement-*.exe
|
node_modules
|
||||||
export*.json
|
config.json
|
||||||
secret.js
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
native.node
|
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -1,9 +1,13 @@
|
|||||||
# 原神成就导出工具
|
<div align="center">
|
||||||
|
|
||||||
|
# YaeAchievement
|
||||||
|
|
||||||
    
|
    
|
||||||
|
|
||||||
- 支持导出所有成就
|
</div>
|
||||||
- 支持官服,B服与国际服
|
|
||||||
|
- 支持导出所有类别的成就
|
||||||
|
- 支持官服,渠道服与国际服
|
||||||
- 支持导出至[椰羊](https://cocogoat.work/achievement)、[SnapGenshin](https://github.com/DGP-Studio/Snap.Genshin)、[Paimon.moe](https://paimon.moe/achievement/)、[Seelie.me](https://seelie.me/achievements)和表格文件(csv)
|
- 支持导出至[椰羊](https://cocogoat.work/achievement)、[SnapGenshin](https://github.com/DGP-Studio/Snap.Genshin)、[Paimon.moe](https://paimon.moe/achievement/)、[Seelie.me](https://seelie.me/achievements)和表格文件(csv)
|
||||||
- 没有窗口大小、游戏语言等要求
|
- 没有窗口大小、游戏语言等要求
|
||||||
|
|
||||||
|
|||||||
9
app.js
9
app.js
@@ -1,14 +1,14 @@
|
|||||||
const proxy = require("udp-proxy")
|
const proxy = require("udp-proxy")
|
||||||
const cp = require("child_process")
|
const cp = require("child_process")
|
||||||
const cloud = require("./secret")
|
|
||||||
const appcenter = require("./appcenter")
|
const appcenter = require("./appcenter")
|
||||||
const regionServer = require("./regionServer")
|
const regionServer = require("./regionServer")
|
||||||
|
const cloud = require("./generated/secret")
|
||||||
const {
|
const {
|
||||||
initConfig, splitPacket, upload, decodeProto, log, setupHost, KPacket, debug, checkUpdate,
|
initConfig, splitPacket, upload, decodeProto, log, setupHost, KPacket, debug, checkUpdate,
|
||||||
brotliCompressSync, brotliDecompressSync, checkGameIsRunning, checkPortIsUsing
|
brotliCompressSync, brotliDecompressSync, checkGameIsRunning, checkPortIsUsing
|
||||||
} = require("./utils")
|
} = require("./utils")
|
||||||
const { exportData } = require("./export")
|
const { exportData } = require("./export")
|
||||||
const { enablePrivilege, pause } = require("./native")
|
const { enablePrivilege, pause } = require("./generated/native")
|
||||||
|
|
||||||
const onExit = () => {
|
const onExit = () => {
|
||||||
setupHost(true)
|
setupHost(true)
|
||||||
@@ -123,7 +123,7 @@ const onExit = () => {
|
|||||||
log(`请求ID: ${response.headers["x-api-requestid"]}`)
|
log(`请求ID: ${response.headers["x-api-requestid"]}`)
|
||||||
log("请联系开发者以获取帮助")
|
log("请联系开发者以获取帮助")
|
||||||
} else {
|
} else {
|
||||||
const proto = await decodeProto(data,"AllAchievement")
|
const proto = await decodeProto(data, "Notify1")
|
||||||
await exportData(proto)
|
await exportData(proto)
|
||||||
}
|
}
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
@@ -145,9 +145,10 @@ const onExit = () => {
|
|||||||
switch(buf.readUInt32BE(0)) {
|
switch(buf.readUInt32BE(0)) {
|
||||||
case 325:
|
case 325:
|
||||||
createMonitor()
|
createMonitor()
|
||||||
debug("服务端握手应答")
|
debug("Connection established.")
|
||||||
break
|
break
|
||||||
case 404:
|
case 404:
|
||||||
|
debug("Connection terminated.")
|
||||||
lastRecvTimestamp = parseInt(Date.now() / 1000) - 2333
|
lastRecvTimestamp = parseInt(Date.now() / 1000) - 2333
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const axios = require("axios")
|
const axios = require("axios")
|
||||||
const crypto = require("crypto")
|
const crypto = require("crypto")
|
||||||
const { version } = require("./version")
|
const { version } = require("./version")
|
||||||
const { getDeviceID, getDeviceInfo } = require("./native")
|
const { getDeviceID, getDeviceInfo } = require("./generated/native")
|
||||||
|
|
||||||
const getTimestamp = (d = new Date()) => {
|
const getTimestamp = (d = new Date()) => {
|
||||||
const p = i => i.toString().padStart(2, "0")
|
const p = i => i.toString().padStart(2, "0")
|
||||||
|
|||||||
80
export.js
80
export.js
@@ -1,14 +1,14 @@
|
|||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const axios = require("axios")
|
const axios = require("axios")
|
||||||
const readline = require("readline")
|
const readline = require("readline")
|
||||||
const { randomUUID } = require("crypto")
|
const { version } = require("./version")
|
||||||
const { loadCache, log, openUrl } = require("./utils")
|
const { loadCache, log, openUrl } = require("./utils")
|
||||||
const { checkSnapFastcall, copyToClipboard } = require("./native")
|
const { checkSnapFastcall, copyToClipboard } = require("./generated/native")
|
||||||
|
|
||||||
const exportToSeelie = proto => {
|
const exportToSeelie = proto => {
|
||||||
const out = { achievements: {} }
|
const out = { achievements: {} }
|
||||||
proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id}) => {
|
proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id}) => {
|
||||||
out.achievements[id] = { done: true }
|
out.achievements[id === 81222 ? 81219 : id] = { done: true }
|
||||||
})
|
})
|
||||||
const fp = `./export-${Date.now()}-seelie.json`
|
const fp = `./export-${Date.now()}-seelie.json`
|
||||||
fs.writeFileSync(fp, JSON.stringify(out))
|
fs.writeFileSync(fp, JSON.stringify(out))
|
||||||
@@ -19,58 +19,52 @@ const exportToPaimon = async proto => {
|
|||||||
const out = { achievement: {} }
|
const out = { achievement: {} }
|
||||||
const data = await loadCache()
|
const data = await loadCache()
|
||||||
proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id}) => {
|
proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id}) => {
|
||||||
const gid = data["a"][id]
|
const gid = data["a"][id]["g"]
|
||||||
if (out.achievement[gid] === undefined) {
|
if (out.achievement[gid] === undefined) {
|
||||||
out.achievement[gid] = {}
|
out.achievement[gid] = {}
|
||||||
}
|
}
|
||||||
out.achievement[gid][id] = true
|
out.achievement[gid][id === 81222 ? 81219 : id] = true
|
||||||
})
|
})
|
||||||
const fp = `./export-${Date.now()}-paimon.json`
|
const fp = `./export-${Date.now()}-paimon.json`
|
||||||
fs.writeFileSync(fp, JSON.stringify(out))
|
fs.writeFileSync(fp, JSON.stringify(out))
|
||||||
log(`导出为文件: ${fp}`)
|
log(`导出为文件: ${fp}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportToSnapGenshin = async proto => {
|
const UIAF = proto => {
|
||||||
const out = []
|
const out = {
|
||||||
proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({id, finishTimestamp}) => {
|
info: {
|
||||||
out.push({
|
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,
|
id: id,
|
||||||
timestamp: finishTimestamp
|
timestamp: finishTimestamp,
|
||||||
|
current: current
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportToSnapGenshin = async proto => {
|
||||||
if (checkSnapFastcall()) {
|
if (checkSnapFastcall()) {
|
||||||
const json = JSON.stringify(out)
|
const result = UIAF(proto)
|
||||||
const path = `${process.env.TMP}/YaeAchievement-export-${randomUUID()}`
|
const json = JSON.stringify(result)
|
||||||
fs.writeFileSync(path, json)
|
copyToClipboard(json)
|
||||||
openUrl(`snapgenshin://achievement/import/file?path=\"${path}\"`)
|
openUrl(`snapgenshin://achievement/import/uiaf`)
|
||||||
log("在 SnapGenshin 进行下一步操作")
|
log("在 SnapGenshin 进行下一步操作")
|
||||||
} else {
|
} else {
|
||||||
const json = JSON.stringify(out, null, 2)
|
log("请更新 SnapGenshin 后重试")
|
||||||
copyToClipboard(json)
|
|
||||||
log("导出内容已复制到剪贴板")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportToCocogoat = async proto => {
|
const exportToCocogoat = async proto => {
|
||||||
const out = {
|
const result = UIAF(proto)
|
||||||
achievements: []
|
const response = await axios.post(`https://77.cocogoat.work/v1/memo?source=${encodeURI("全部成就")}`, result).catch(_ => {
|
||||||
}
|
|
||||||
const data = await loadCache()
|
|
||||||
const p = i => i.toString().padStart(2, "0")
|
|
||||||
const getDate = ts => {
|
|
||||||
const d = new Date(parseInt(`${ts}000`))
|
|
||||||
return `${d.getFullYear()}/${p(d.getMonth()+1)}/${p(d.getDate())}`
|
|
||||||
}
|
|
||||||
proto.list.filter(a => a.status === 3 || a.status === 2).forEach(({current, finishTimestamp, id, require}) => {
|
|
||||||
const curAch = data["a"][id]
|
|
||||||
out.achievements.push({
|
|
||||||
id: id,
|
|
||||||
status: current === undefined || current === 0 || curAch["p"] === undefined ? `${require}/${require}` : `${current}/${require}`,
|
|
||||||
categoryId: curAch["g"],
|
|
||||||
date: getDate(finishTimestamp)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const response = await axios.post(`https://77.cocogoat.work/v1/memo?source=${encodeURI("全部成就")}`, out).catch(_ => {
|
|
||||||
console.log("网络错误,请检查网络后重试 (26-1)")
|
console.log("网络错误,请检查网络后重试 (26-1)")
|
||||||
process.exit(261)
|
process.exit(261)
|
||||||
})
|
})
|
||||||
@@ -82,7 +76,7 @@ const exportToCocogoat = async proto => {
|
|||||||
if (retcode > 32) {
|
if (retcode > 32) {
|
||||||
log("在浏览器内进行下一步操作")
|
log("在浏览器内进行下一步操作")
|
||||||
} else {
|
} else {
|
||||||
log(`导出失败,请联系开发者以获取帮助 (26-3-${retcode})`)
|
log(`打开此链接以进行下一步操作: https://cocogoat.work/achievement?memo=${response.data.key}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +123,17 @@ const exportData = async proto => {
|
|||||||
const question = (query) => new Promise(resolve => {
|
const question = (query) => new Promise(resolve => {
|
||||||
rl.question(query, resolve)
|
rl.question(query, resolve)
|
||||||
})
|
})
|
||||||
const chosen = await question("导出至: \n[0] 椰羊 (https://cocogoat.work/achievement)\n[1] SnapGenshin\n[2] Paimon.moe\n[3] Seelie.me\n[4] 表格文件 (默认)\n输入一个数字(0-4): ")
|
const chosen = await question(
|
||||||
|
[
|
||||||
|
"导出至: ",
|
||||||
|
"[0] 椰羊 (https://cocogoat.work/achievement)",
|
||||||
|
"[1] SnapGenshin",
|
||||||
|
"[2] Paimon.moe",
|
||||||
|
"[3] Seelie.me",
|
||||||
|
"[4] 表格文件 (默认)",
|
||||||
|
"输入一个数字(0-4): "
|
||||||
|
].join("\n")
|
||||||
|
)
|
||||||
rl.close()
|
rl.close()
|
||||||
switch (chosen.trim()) {
|
switch (chosen.trim()) {
|
||||||
case "0":
|
case "0":
|
||||||
|
|||||||
10
native.d.ts
vendored
10
native.d.ts
vendored
@@ -1,10 +0,0 @@
|
|||||||
export function selectGameExecutable(): string
|
|
||||||
export function checkGameIsRunning(processName: string): boolean
|
|
||||||
export function whoUseThePort(port: number): object
|
|
||||||
export function copyToClipboard(value: string): any
|
|
||||||
export function checkSnapFastcall(): boolean
|
|
||||||
export function enablePrivilege(): any
|
|
||||||
export function getDeviceInfo(): any
|
|
||||||
export function getDeviceID(): string
|
|
||||||
export function openUrl(url: string): number
|
|
||||||
export function pause(): any
|
|
||||||
1
native/.gitignore
vendored
1
native/.gitignore
vendored
@@ -3,3 +3,4 @@ CMakeLists.txt
|
|||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
node_modules
|
node_modules
|
||||||
build
|
build
|
||||||
|
src/homu.cpp
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"src/wmi/wmi.cpp",
|
"src/wmi/wmi.cpp",
|
||||||
"src/wmi/wmi.hpp",
|
"src/wmi/wmi.hpp",
|
||||||
"src/wmi/unistd.h",
|
"src/wmi/unistd.h",
|
||||||
|
"src/VMProtectSDK.h",
|
||||||
"src/wmi/wmiresult.cpp",
|
"src/wmi/wmiresult.cpp",
|
||||||
"src/wmi/wmiresult.hpp",
|
"src/wmi/wmiresult.hpp",
|
||||||
"src/wmi/wmiclasses.hpp",
|
"src/wmi/wmiclasses.hpp",
|
||||||
@@ -33,7 +34,12 @@
|
|||||||
"AdditionalOptions": [
|
"AdditionalOptions": [
|
||||||
"-std:c++latest",
|
"-std:c++latest",
|
||||||
"-DUNICODE",
|
"-DUNICODE",
|
||||||
"-sdl"
|
"-sdl",
|
||||||
|
"-O2",
|
||||||
|
"-Ot",
|
||||||
|
"-Oi",
|
||||||
|
"-GL",
|
||||||
|
"-Gw"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node-gyp rebuild && copy .\\build\\Release\\native.node ..\\genshin-export\\",
|
"build": "node-gyp build && copy .\\build\\Release\\native.node ..\\genshin-export\\generated",
|
||||||
"build-for-win7": "node-gyp rebuild --target=v14.17.0 && copy .\\build\\Release\\native.node ..\\genshin-export\\"
|
"rebuild": "node-gyp rebuild && copy .\\build\\Release\\native.node ..\\genshin-export\\generated",
|
||||||
|
"build-for-win7": "node-gyp rebuild --target=v14.17.0 && copy .\\build\\Release\\native.node ..\\genshin-export\\generated"
|
||||||
},
|
},
|
||||||
"gypfile": true,
|
"gypfile": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <TlHelp32.h>
|
#include <TlHelp32.h>
|
||||||
|
|
||||||
using std::string, std::wstring, std::cout, std::to_string;
|
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;
|
using Napi::Object, Napi::Env, Napi::Function, Napi::Value, Napi::CallbackInfo, Napi::TypeError, Napi::Error;
|
||||||
|
|
||||||
typedef unsigned char byte;
|
typedef unsigned char byte;
|
||||||
|
typedef unsigned int ui;
|
||||||
typedef unsigned long ul;
|
typedef unsigned long ul;
|
||||||
typedef unsigned long long ull;
|
typedef unsigned long long ull;
|
||||||
|
|||||||
11
native/src/homu.h
Normal file
11
native/src/homu.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//#include "homu.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "define.h"
|
#include "define.h"
|
||||||
#include "wmi/wmi.hpp"
|
#include "wmi/wmi.hpp"
|
||||||
@@ -12,7 +13,7 @@ using Wmi::Win32_ComputerSystem, Wmi::Win32_OperatingSystem, Wmi::retrieveWmi;
|
|||||||
|
|
||||||
namespace native {
|
namespace native {
|
||||||
|
|
||||||
Value checkGameIsRunning(const CallbackInfo &info) {
|
Value CheckGameIsRunning(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
if (info.Length() != 1 || !info[0].IsString()) {
|
if (info.Length() != 1 || !info[0].IsString()) {
|
||||||
TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
|
TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
|
||||||
@@ -34,7 +35,7 @@ namespace native {
|
|||||||
return Napi::Boolean::New(env, isRunning);
|
return Napi::Boolean::New(env, isRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value selectGameExecutable(const CallbackInfo &info) {
|
Value SelectGameExecutable(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
Napi::String path;
|
Napi::String path;
|
||||||
if (OpenFile(env, path) != ERROR_SUCCESS) {
|
if (OpenFile(env, path) != ERROR_SUCCESS) {
|
||||||
@@ -44,7 +45,7 @@ namespace native {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value whoUseThePort(const CallbackInfo &info) {
|
Value WhoUseThePort(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
if (info.Length() != 1 || !info[0].IsNumber()) {
|
if (info.Length() != 1 || !info[0].IsNumber()) {
|
||||||
TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
|
TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
|
||||||
@@ -89,7 +90,7 @@ namespace native {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value getDeviceID(const CallbackInfo &info) {
|
Value GetDeviceID(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
wstring wd;
|
wstring wd;
|
||||||
if (RegUtils::GetString(HKEY_CURRENT_USER, L"SOFTWARE\\miHoYoSDK", L"MIHOYOSDK_DEVICE_ID", wd) != ERROR_SUCCESS) {
|
if (RegUtils::GetString(HKEY_CURRENT_USER, L"SOFTWARE\\miHoYoSDK", L"MIHOYOSDK_DEVICE_ID", wd) != ERROR_SUCCESS) {
|
||||||
@@ -99,7 +100,7 @@ namespace native {
|
|||||||
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));
|
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) {
|
Value GetDeviceInfo(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
HDC desktop = GetDC(nullptr);
|
HDC desktop = GetDC(nullptr);
|
||||||
int sw = GetDeviceCaps(desktop, DESKTOPHORZRES);
|
int sw = GetDeviceCaps(desktop, DESKTOPHORZRES);
|
||||||
@@ -140,13 +141,34 @@ namespace native {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value enablePrivilege(const CallbackInfo &info) {
|
Value EnablePrivilege(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
EnablePrivilege(env, L"SeDebugPrivilege");
|
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();
|
return env.Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value copyToClipboard(const CallbackInfo &info) {
|
Value CopyToClipboard(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
string text = info[0].As<Napi::String>().Utf8Value();
|
string text = info[0].As<Napi::String>().Utf8Value();
|
||||||
if (OpenClipboard(nullptr)) {
|
if (OpenClipboard(nullptr)) {
|
||||||
@@ -162,38 +184,64 @@ namespace native {
|
|||||||
return env.Undefined();
|
return env.Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value pause(const CallbackInfo &info) {
|
Value Pause(const CallbackInfo &info) {
|
||||||
while(!_kbhit()) {
|
while(!_kbhit()) {
|
||||||
Sleep(10);
|
Sleep(10);
|
||||||
}
|
}
|
||||||
return info.Env().Undefined();
|
return info.Env().Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value openUrl(const CallbackInfo &info) {
|
Value OpenUrl(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
wstring url = StringToWString(info[0].As<Napi::String>().Utf8Value());
|
wstring url = StringToWString(info[0].As<Napi::String>().Utf8Value());
|
||||||
HINSTANCE retcode = ShellExecute(GetConsoleWindow(), L"open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
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)
|
return Napi::Number::New(env, (INT_PTR)retcode); // NOLINT(cppcoreguidelines-narrowing-conversions)
|
||||||
}
|
}
|
||||||
|
|
||||||
Value checkSnapFastcall(const CallbackInfo &info) {
|
Value CheckSnapFastcall(const CallbackInfo &info) {
|
||||||
Env env = info.Env();
|
Env env = info.Env();
|
||||||
wstring queryResult;
|
wstring queryResult;
|
||||||
RegUtils::GetString(HKEY_CLASSES_ROOT, L"snapgenshin", L"", queryResult);
|
RegUtils::GetString(HKEY_CLASSES_ROOT, L"snapgenshin", L"", queryResult);
|
||||||
return Napi::Boolean::New(env, wcscmp(queryResult.c_str(), L"URL:snapgenshin") == 0);
|
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) {
|
Object init(Env env, Object exports) {
|
||||||
exports.Set("pause", Function::New(env, pause));
|
exports.Set("pause", Function::New(env, Pause));
|
||||||
exports.Set("openUrl", Function::New(env, openUrl));
|
exports.Set("openUrl", Function::New(env, OpenUrl));
|
||||||
exports.Set("getDeviceID", Function::New(env, getDeviceID));
|
//exports.Set("homuInit", Function::New(env, Initialize));
|
||||||
exports.Set("getDeviceInfo", Function::New(env, getDeviceInfo));
|
exports.Set("getDeviceID", Function::New(env, GetDeviceID));
|
||||||
exports.Set("whoUseThePort", Function::New(env, whoUseThePort));
|
exports.Set("getMACAddress", Function::New(env, GetMACAddress));
|
||||||
exports.Set("copyToClipboard", Function::New(env, copyToClipboard));
|
exports.Set("getDeviceInfo", Function::New(env, GetDeviceInfo));
|
||||||
exports.Set("enablePrivilege", Function::New(env, enablePrivilege));
|
exports.Set("whoUseThePort", Function::New(env, WhoUseThePort));
|
||||||
exports.Set("checkSnapFastcall", Function::New(env, checkSnapFastcall));
|
exports.Set("copyToClipboard", Function::New(env, CopyToClipboard));
|
||||||
exports.Set("checkGameIsRunning", Function::New(env, checkGameIsRunning));
|
exports.Set("enablePrivilege", Function::New(env, EnablePrivilege));
|
||||||
exports.Set("selectGameExecutable", Function::New(env, selectGameExecutable));
|
exports.Set("checkSnapFastcall", Function::New(env, CheckSnapFastcall));
|
||||||
|
exports.Set("checkGameIsRunning", Function::New(env, CheckGameIsRunning));
|
||||||
|
exports.Set("selectGameExecutable", Function::New(env, SelectGameExecutable));
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ void Log(Env env, const wstring &msg) {
|
|||||||
logFunc.Call({ Napi::String::New(env, WStringToString(msg)) });
|
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) {
|
LSTATUS OpenFile(Env env, Napi::String &result, HWND parent) {
|
||||||
OPENFILENAME open;
|
OPENFILENAME open;
|
||||||
ZeroMemory(&open, sizeof(open));
|
ZeroMemory(&open, sizeof(open));
|
||||||
@@ -48,29 +60,3 @@ LSTATUS OpenFile(Env env, Napi::String &result, HWND parent) {
|
|||||||
return ERROR_ERRORS_ENCOUNTERED;
|
return ERROR_ERRORS_ENCOUNTERED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL EnablePrivilege(Env env, const wstring &name) {
|
|
||||||
HANDLE hToken;
|
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
|
|
||||||
Error::New(env, "OpenProcessToken error: " + to_string(GetLastError())).ThrowAsJavaScriptException();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
TOKEN_PRIVILEGES tp;
|
|
||||||
ZeroMemory(&tp, sizeof(tp));
|
|
||||||
tp.PrivilegeCount = 1;
|
|
||||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
||||||
if (!LookupPrivilegeValue(nullptr, name.c_str(), &tp.Privileges[0].Luid)) {
|
|
||||||
Error::New(env, "LookupPrivilegeValue error: " + to_string(GetLastError())).ThrowAsJavaScriptException();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, nullptr)) {
|
|
||||||
Error::New(env, "AdjustTokenPrivileges error: " + to_string(GetLastError())).ThrowAsJavaScriptException();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
|
|
||||||
Error::New(env, "The token does not have the specified privilege.").ThrowAsJavaScriptException();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
CloseHandle(hToken);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
string WStringToString(const wstring &src, UINT codePage = CP_ACP);
|
string WStringToString(const wstring &src, UINT codePage = CP_ACP);
|
||||||
wstring StringToWString(const string &src, UINT codePage = CP_ACP);
|
wstring StringToWString(const string &src, UINT codePage = CP_ACP);
|
||||||
LSTATUS OpenFile(Env env, Napi::String &result, HWND parent = GetConsoleWindow());
|
LSTATUS OpenFile(Env env, Napi::String &result, HWND parent = GetConsoleWindow());
|
||||||
BOOL EnablePrivilege(Env env, const wstring &name);
|
char* ToHex(const char *bin, int binsz, char **result);
|
||||||
void Log(Env env, const string &msg);
|
void Log(Env env, const string &msg);
|
||||||
void Log(Env env, const wstring &msg);
|
void Log(Env env, const wstring &msg);
|
||||||
|
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
message AllAchievement {
|
|
||||||
repeated Achievement list = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Achievement {
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
INVALID = 0;
|
|
||||||
UNFINISHED = 1;
|
|
||||||
FINISHED = 2;
|
|
||||||
REWARD_TAKEN = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 id = 1;
|
|
||||||
Status status = 2;
|
|
||||||
uint32 current = 3;
|
|
||||||
uint32 require = 4;
|
|
||||||
uint32 finish_timestamp = 5;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryCurRegion {
|
|
||||||
int32 retcode = 1;
|
|
||||||
string message = 2;
|
|
||||||
msg0 info = 3;
|
|
||||||
oneof group {
|
|
||||||
msg3 field2 = 4;
|
|
||||||
msg4 field3 = 5;
|
|
||||||
}
|
|
||||||
bytes field4 = 11;
|
|
||||||
bytes field5 = 12;
|
|
||||||
bytes field6 = 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryRegionList {
|
|
||||||
int32 retcode = 1;
|
|
||||||
bytes field0 = 5;
|
|
||||||
bytes field1 = 6;
|
|
||||||
bool field2 = 7;
|
|
||||||
repeated msg2 list = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message msg0 {
|
|
||||||
string ip = 1;
|
|
||||||
uint32 port = 2;
|
|
||||||
string field0 = 3;
|
|
||||||
string field1 = 7;
|
|
||||||
string field2 = 8;
|
|
||||||
string field3 = 9;
|
|
||||||
string field4 = 10;
|
|
||||||
string field5 = 11;
|
|
||||||
string field6 = 12;
|
|
||||||
string field7 = 13;
|
|
||||||
uint32 field8 = 14;
|
|
||||||
string field9 = 16;
|
|
||||||
uint32 fieldA = 18;
|
|
||||||
string fieldB = 19;
|
|
||||||
string fieldC = 20;
|
|
||||||
msg1 fieldD = 22;
|
|
||||||
bytes fieldE = 23;
|
|
||||||
string fieldF = 24;
|
|
||||||
string fieldG = 26;
|
|
||||||
string fieldH = 27;
|
|
||||||
bool fieldI = 28;
|
|
||||||
string fieldJ = 29;
|
|
||||||
string fieldK = 30;
|
|
||||||
string fieldL = 31;
|
|
||||||
string fieldM = 32;
|
|
||||||
string fieldN = 33;
|
|
||||||
string fieldO = 34;
|
|
||||||
msg1 fieldP = 35;
|
|
||||||
}
|
|
||||||
|
|
||||||
message msg1 {
|
|
||||||
uint32 field0 = 1;
|
|
||||||
bool field1 = 2;
|
|
||||||
string field2 = 3;
|
|
||||||
string field3 = 4;
|
|
||||||
string field4 = 5;
|
|
||||||
string field5 = 6;
|
|
||||||
string field6 = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message msg2 {
|
|
||||||
string field0 = 1;
|
|
||||||
string field1 = 2;
|
|
||||||
string field2 = 3;
|
|
||||||
string url = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message msg3 {
|
|
||||||
string field0 = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message msg4 {
|
|
||||||
uint32 field0 = 1;
|
|
||||||
uint32 field1 = 2;
|
|
||||||
string field2 = 3;
|
|
||||||
string field3 = 4;
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ const https = require("https")
|
|||||||
const axios = require("axios")
|
const axios = require("axios")
|
||||||
const { decodeProto, encodeProto, debug } = require("./utils")
|
const { decodeProto, encodeProto, debug } = require("./utils")
|
||||||
const path = require("path")
|
const path = require("path")
|
||||||
const cert = path.join(__dirname, "./cert/root.p12")
|
const cert = path.join(__dirname, "./cache/cert/root.p12")
|
||||||
|
|
||||||
const preparedRegions = {}
|
const preparedRegions = {}
|
||||||
let currentProxy = undefined
|
let currentProxy = undefined
|
||||||
|
|||||||
29
utils.js
29
utils.js
@@ -2,8 +2,8 @@ const fs = require("fs")
|
|||||||
const dns = require("dns")
|
const dns = require("dns")
|
||||||
const ini = require("ini")
|
const ini = require("ini")
|
||||||
const zlib = require("zlib")
|
const zlib = require("zlib")
|
||||||
const cloud = require("./secret")
|
const cloud = require("./generated/secret")
|
||||||
const native = require("./native")
|
const native = require("./generated/native")
|
||||||
const readline = require("readline")
|
const readline = require("readline")
|
||||||
const protobuf = require("protobufjs")
|
const protobuf = require("protobufjs")
|
||||||
const { version } = require("./version")
|
const { version } = require("./version")
|
||||||
@@ -11,17 +11,16 @@ const { promisify } = require("util")
|
|||||||
const { createHash } = require("crypto")
|
const { createHash } = require("crypto")
|
||||||
const path = require("path")
|
const path = require("path")
|
||||||
const { uploadEvent } = require("./appcenter")
|
const { uploadEvent } = require("./appcenter")
|
||||||
const messages = path.join(__dirname, "./proto/Messages.proto")
|
|
||||||
|
|
||||||
const encodeProto = (object, name) => protobuf.load(messages).then(r => {
|
const messages = protobuf.loadSync(path.join(__dirname, "./generated/Messages.proto"))
|
||||||
const msgType = r.lookupType(name)
|
|
||||||
|
const encodeProto = (object, name) => {
|
||||||
|
const msgType = messages.lookupType(name)
|
||||||
const msgInst = msgType.create(object)
|
const msgInst = msgType.create(object)
|
||||||
return msgType.encode(msgInst).finish()
|
return msgType.encode(msgInst).finish()
|
||||||
})
|
}
|
||||||
|
|
||||||
const decodeProto = (buf, name) => protobuf.load(messages).then(r => {
|
const decodeProto = (buf, name) => messages.lookupType(name).decode(buf)
|
||||||
return r.lookupType(name).decode(buf)
|
|
||||||
})
|
|
||||||
|
|
||||||
const checkPath = path => new Promise((resolve, reject) => {
|
const checkPath = path => new Promise((resolve, reject) => {
|
||||||
if (!fs.existsSync(`${path}/UnityPlayer.dll`) || !fs.existsSync(`${path}/pkg_version`)) {
|
if (!fs.existsSync(`${path}/UnityPlayer.dll`) || !fs.existsSync(`${path}/pkg_version`)) {
|
||||||
@@ -206,11 +205,15 @@ const upload = async data => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checkUpdate = async () => {
|
const checkUpdate = async () => {
|
||||||
const data = (await cloud.get("/latest-version")).data
|
const data = (await cloud.fetchBucket("/latest.json")).data
|
||||||
if (data["vc"] !== version.code) {
|
if (data["vc"] !== version.code) {
|
||||||
log(`有可用更新: ${version.name} => ${data["vn"]}`)
|
console.log(`有可用更新: ${version.name} => ${data["vn"]}`)
|
||||||
log(`更新内容: \n${data["ds"]}`)
|
console.log(`更新内容: \n${data["ds"]}`)
|
||||||
log("下载地址: https://github.com/HolographicHat/genshin-achievement-export/releases\n")
|
console.log("下载地址: https://github.com/HolographicHat/YaeAchievement/releases")
|
||||||
|
if (data["fc"] === true) {
|
||||||
|
console.log(" * 这是一次强制更新")
|
||||||
|
process.exit(410)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const version = {
|
const version = {
|
||||||
code: 7,
|
code: 8,
|
||||||
name: "1.6.50",
|
name: "1.7.0",
|
||||||
isDev: true
|
isDev: false
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { version }
|
module.exports = { version }
|
||||||
|
|||||||
Reference in New Issue
Block a user