mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-18 20:33:19 +08:00
select game by open file dialog, optimize code and message
This commit is contained in:
112
utils.js
112
utils.js
@@ -1,21 +1,18 @@
|
|||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const dns = require("dns")
|
const dns = require("dns")
|
||||||
const ini = require("ini")
|
const ini = require("ini")
|
||||||
const util = require("util")
|
|
||||||
const zlib = require("zlib")
|
const zlib = require("zlib")
|
||||||
const cloud = require("./secret")
|
const cloud = require("./secret")
|
||||||
|
const native = require("./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")
|
||||||
|
const { promisify } = require("util")
|
||||||
const { createHash } = require("crypto")
|
const { createHash } = require("crypto")
|
||||||
const path = require("path")
|
const path = require("path")
|
||||||
const messages = path.join(__dirname, "./proto/Messages.proto")
|
const messages = path.join(__dirname, "./proto/Messages.proto")
|
||||||
let axios = require("axios")
|
let axios = require("axios")
|
||||||
|
|
||||||
const sleep = ms => new Promise(resolve => {
|
|
||||||
setTimeout(resolve, ms)
|
|
||||||
})
|
|
||||||
|
|
||||||
const encodeProto = (object, name) => protobuf.load(messages).then(r => {
|
const encodeProto = (object, name) => protobuf.load(messages).then(r => {
|
||||||
const msgType = r.lookupType(name)
|
const msgType = r.lookupType(name)
|
||||||
const msgInst = msgType.create(object)
|
const msgInst = msgType.create(object)
|
||||||
@@ -26,44 +23,33 @@ const decodeProto = (buf, name) => protobuf.load(messages).then(r => {
|
|||||||
return r.lookupType(name).decode(buf)
|
return r.lookupType(name).decode(buf)
|
||||||
})
|
})
|
||||||
|
|
||||||
const checkPath = (path, cb) => {
|
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`)) {
|
||||||
throw Error(`路径有误: ${path}`)
|
reject(`路径有误 ${path}`)
|
||||||
} else {
|
} else {
|
||||||
cb(path)
|
resolve(path)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
let conf
|
let conf
|
||||||
|
|
||||||
const initConfig = async () => {
|
const initConfig = async () => {
|
||||||
const configFileName = "./config.json"
|
const configFileName = "./config.json"
|
||||||
const rl = readline.createInterface({
|
|
||||||
input: process.stdin,
|
|
||||||
output: process.stdout
|
|
||||||
})
|
|
||||||
const question = (query) => new Promise(resolve => {
|
|
||||||
rl.question(query, resolve)
|
|
||||||
})
|
|
||||||
const lookup = util.promisify(dns.lookup).bind(dns)
|
|
||||||
if (fs.existsSync(configFileName)) {
|
if (fs.existsSync(configFileName)) {
|
||||||
conf = JSON.parse(fs.readFileSync(configFileName, "utf-8"))
|
conf = JSON.parse(fs.readFileSync(configFileName, "utf-8"))
|
||||||
} else {
|
} else {
|
||||||
const p = await question("原神主程序(YuanShen.exe或GenshinImpact.exe)所在路径: (支持多个路径, 使用符号'*'分隔)\n")
|
|
||||||
conf = {
|
conf = {
|
||||||
path: [],
|
path: [],
|
||||||
offlineResource: false,
|
offlineResource: false,
|
||||||
customCDN: ""
|
customCDN: ""
|
||||||
}
|
}
|
||||||
p.split("*").forEach(s => {
|
const p = path.dirname(native.selectGameExecutable())
|
||||||
checkPath(s, () => {
|
await checkPath(p).catch(reason => {
|
||||||
if (!conf.path.includes(s)) {
|
console.log(reason)
|
||||||
conf.path.push(s)
|
process.exit(1)
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
conf.path.push(p)
|
||||||
fs.writeFileSync(configFileName, JSON.stringify(conf, null, 2))
|
fs.writeFileSync(configFileName, JSON.stringify(conf, null, 2))
|
||||||
rl.close()
|
|
||||||
}
|
}
|
||||||
if (conf.proxy !== undefined) {
|
if (conf.proxy !== undefined) {
|
||||||
axios = axios.create({
|
axios = axios.create({
|
||||||
@@ -71,34 +57,61 @@ const initConfig = async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (conf.path.length === 1) {
|
if (conf.path.length === 1) {
|
||||||
checkPath(conf.path[0], p => {
|
await checkPath(conf.path[0]).catch(reason => {
|
||||||
|
console.log(reason)
|
||||||
|
process.exit(1)
|
||||||
|
}).then(p => {
|
||||||
conf.path = p
|
conf.path = p
|
||||||
})
|
})
|
||||||
} else {
|
} 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 idx = await question(`选择客户端: \n${conf.path.map((s, i) => {
|
||||||
const fp = fs.existsSync(`${s}/GenshinImpact.exe`) ? `${s}\\GenshinImpact.exe` : `${s}\\YuanShen.exe`
|
const fp = fs.existsSync(`${s}/GenshinImpact.exe`) ? `${s}\\GenshinImpact.exe` : `${s}\\YuanShen.exe`
|
||||||
return `[${i}] ${fp}`
|
return `[${i}] ${fp}`
|
||||||
}).join("\n")}\n> `)
|
}).join("\n")}\n> `)
|
||||||
checkPath(conf.path[parseInt(idx)], p => {
|
await checkPath(conf.path[parseInt(idx)]).catch(reason => {
|
||||||
|
console.log(reason)
|
||||||
|
process.exit(1)
|
||||||
|
}).then(p => {
|
||||||
conf.path = p
|
conf.path = p
|
||||||
})
|
})
|
||||||
|
rl.close()
|
||||||
}
|
}
|
||||||
rl.close()
|
|
||||||
conf.isOversea = fs.existsSync(conf.path + "/GenshinImpact.exe")
|
conf.isOversea = fs.existsSync(conf.path + "/GenshinImpact.exe")
|
||||||
conf.dataDir = conf.isOversea ? conf.path + "/GenshinImpact_Data" : conf.path + "/YuanShen_Data"
|
conf.dataDir = conf.isOversea ? conf.path + "/GenshinImpact_Data" : conf.path + "/YuanShen_Data"
|
||||||
const readGameRes = (path) => fs.readFileSync(conf.dataDir + path)
|
const readGameRes = (path) => fs.readFileSync(conf.dataDir + path, "utf-8")
|
||||||
// noinspection JSUnresolvedVariable
|
const genshinConf = ini.parse(fs.readFileSync(`${conf.path}/config.ini`, "utf-8"))["General"]
|
||||||
const genshinConf = ini.parse(fs.readFileSync(conf.path + "/config.ini", "utf-8")).General
|
conf.channel = genshinConf["channel"]
|
||||||
conf.channel = genshinConf.channel
|
conf.subChannel = genshinConf["sub_channel"]
|
||||||
// noinspection JSUnresolvedVariable
|
conf.version = readGameRes("/Persistent/ChannelName") + readGameRes("/Persistent/ScriptVersion")
|
||||||
conf.subChannel = genshinConf.sub_channel
|
conf.executable = conf.isOversea ? `${conf.path}/GenshinImpact.exe` : `${conf.path}/YuanShen.exe`
|
||||||
conf.version = readGameRes("/Persistent/ChannelName").toString() + readGameRes("/Persistent/ScriptVersion").toString()
|
|
||||||
conf.executable = conf.isOversea ? conf.path + "/GenshinImpact.exe" : conf.path + "/YuanShen.exe"
|
|
||||||
conf.dispatchUrl = `dispatch${conf.isOversea ? "os" : "cn"}global.yuanshen.com`
|
conf.dispatchUrl = `dispatch${conf.isOversea ? "os" : "cn"}global.yuanshen.com`
|
||||||
conf.dispatchIP = (await lookup(conf.dispatchUrl, 4)).address
|
conf.dispatchIP = (await promisify(dns.lookup).bind(dns)(conf.dispatchUrl, 4)).address
|
||||||
return conf
|
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 splitPacket = buf => {
|
const splitPacket = buf => {
|
||||||
let offset = 0
|
let offset = 0
|
||||||
let arr = []
|
let arr = []
|
||||||
@@ -152,11 +165,11 @@ const checkCDN = async () => {
|
|||||||
await axios.head(cdnUrlFormat.format("github/fetch", ".gitignore"))
|
await axios.head(cdnUrlFormat.format("github/fetch", ".gitignore"))
|
||||||
return
|
return
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
throw "没有可用的CDN"
|
throw "网络错误,请检查配置文件/网络后重试 (11-3)"
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadCache = async (fp, repo = "Dimbreath/GenshinData") => {
|
const loadCache = async (fp, repo = "Dimbreath/GenshinData") => {
|
||||||
console.log(cdnUrlFormat.format(repo, fp))
|
log(`预检资源: ${cdnUrlFormat.format(repo, fp)}`)
|
||||||
fs.mkdirSync("./cache", { recursive: true })
|
fs.mkdirSync("./cache", { recursive: true })
|
||||||
const localPath = `./cache/${md5(fp)}`
|
const localPath = `./cache/${md5(fp)}`
|
||||||
if (conf.offlineResource) {
|
if (conf.offlineResource) {
|
||||||
@@ -175,17 +188,17 @@ const loadCache = async (fp, repo = "Dimbreath/GenshinData") => {
|
|||||||
validateStatus: _ => true
|
validateStatus: _ => true
|
||||||
})
|
})
|
||||||
if (headResponse.status === 304) {
|
if (headResponse.status === 304) {
|
||||||
console.log("文件 %s 命中缓存", fp)
|
log("%s 命中缓存", fp)
|
||||||
const etagLength = fd.readUInt8()
|
const etagLength = fd.readUInt8()
|
||||||
return JSON.parse(fd.subarray(1 + etagLength).toString())
|
return JSON.parse(fd.subarray(1 + etagLength).toString())
|
||||||
} else {
|
} else {
|
||||||
console.log("正在下载资源, 请稍后...")
|
log("下载所需资源, 请稍后...")
|
||||||
const response = await axios.get(cdnUrlFormat.format(repo, fp))
|
const response = await axios.get(cdnUrlFormat.format(repo, fp))
|
||||||
const etag = response.headers.etag
|
const etag = response.headers.etag
|
||||||
const str = JSON.stringify(response.data)
|
const str = JSON.stringify(response.data)
|
||||||
const comp = brotliCompressSync(Buffer.concat([Buffer.of(etag.length), Buffer.from(etag), Buffer.from(str)]))
|
const comp = brotliCompressSync(Buffer.concat([Buffer.of(etag.length), Buffer.from(etag), Buffer.from(str)]))
|
||||||
fs.writeFileSync(localPath, comp)
|
fs.writeFileSync(localPath, comp)
|
||||||
console.log("完成.")
|
log("下载完成")
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,9 +222,9 @@ const upload = async data => {
|
|||||||
const checkUpdate = async () => {
|
const checkUpdate = async () => {
|
||||||
const data = (await cloud.get("/latest-version")).data
|
const data = (await cloud.get("/latest-version")).data
|
||||||
if (data["vc"] !== version.code) {
|
if (data["vc"] !== version.code) {
|
||||||
console.log(`有可用更新: ${version.name} => ${data["vn"]}`)
|
log(`有可用更新: ${version.name} => ${data["vn"]}`)
|
||||||
console.log(`更新内容: \n${data["ds"]}`)
|
log(`更新内容: \n${data["ds"]}`)
|
||||||
console.log("下载地址: https://github.com/HolographicHat/genshin-achievement-export/releases\n")
|
log("下载地址: https://github.com/HolographicHat/genshin-achievement-export/releases\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,9 +237,10 @@ const brotliCompressSync = data => zlib.brotliCompressSync(data,{
|
|||||||
|
|
||||||
const brotliDecompressSync = data => zlib.brotliDecompressSync(data)
|
const brotliDecompressSync = data => zlib.brotliDecompressSync(data)
|
||||||
|
|
||||||
let hostsContent = ""
|
let hostsContent = undefined
|
||||||
|
|
||||||
const setupHost = (restore = false) => {
|
const setupHost = (restore = false) => {
|
||||||
|
if (restore && hostsContent === undefined) return
|
||||||
const path = "C:\\Windows\\System32\\drivers\\etc\\hosts"
|
const path = "C:\\Windows\\System32\\drivers\\etc\\hosts"
|
||||||
if (!fs.existsSync(path)) {
|
if (!fs.existsSync(path)) {
|
||||||
fs.writeFileSync(path, "")
|
fs.writeFileSync(path, "")
|
||||||
@@ -291,6 +305,6 @@ class KPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
log, sleep, encodeProto, decodeProto, initConfig, splitPacket, upload, brotliCompressSync, brotliDecompressSync,
|
log, encodeProto, decodeProto, initConfig, splitPacket, upload, brotliCompressSync, brotliDecompressSync,
|
||||||
setupHost, loadCache, debug, checkCDN, checkUpdate, KPacket, cdnUrlFormat
|
setupHost, loadCache, debug, checkCDN, checkUpdate, KPacket, cdnUrlFormat, checkGameIsRunning, checkPortIsUsing
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user