added launching, patching, and ui override
This commit is contained in:
parent
d9fec1193e
commit
f11e84efd7
166
main.js
166
main.js
|
@ -7,16 +7,51 @@ const config = require("./src/config/config");
|
||||||
const { setupTitlebar, attachTitlebarToWindow } = require(
|
const { setupTitlebar, attachTitlebarToWindow } = require(
|
||||||
"custom-electron-titlebar/main",
|
"custom-electron-titlebar/main",
|
||||||
);
|
);
|
||||||
const { isValidOsuFolder } = require("./src/util/osuUtil");
|
const { isValidOsuFolder, getUpdateFiles, getGlobalConfig, getFilesThatNeedUpdate, downloadUpdateFiles, getUserConfig, runOsuWithDevServer, getPatcherUpdates, downloadPatcherUpdates, getUIFiles, downloadUIFiles, replaceUIFile } = require("./src/util/osuUtil");
|
||||||
|
const { formatBytes } = require("./src/util/formattingUtil");
|
||||||
|
const windowName = require("get-window-by-name");
|
||||||
|
const { existsSync } = require("fs");
|
||||||
|
const { runFileDetached } = require("./src/util/executeUtil");
|
||||||
|
|
||||||
// Keep a global reference of the window object, if you don't, the window will
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
// be closed automatically when the JavaScript object is garbage collected.
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
|
let osuCheckInterval;
|
||||||
|
let userOsuPath;
|
||||||
|
let osuLoaded = false;
|
||||||
|
|
||||||
|
let currentUser = undefined;
|
||||||
|
|
||||||
function isDev() {
|
function isDev() {
|
||||||
return !app.isPackaged;
|
return !app.isPackaged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startOsuStatus() {
|
||||||
|
osuCheckInterval = setInterval(async () => {
|
||||||
|
const osuWindowTitle = windowName.getWindowText("osu!.exe");
|
||||||
|
if (osuWindowTitle.length < 0) {
|
||||||
|
console.log("No osu! window found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const firstInstance = osuWindowTitle[0];
|
||||||
|
if (firstInstance) {
|
||||||
|
if (!osuLoaded) {
|
||||||
|
osuLoaded = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
const patcherExecuteable = path.join(userOsuPath, "EZPPLauncher", "patcher.exe");
|
||||||
|
if (existsSync(patcherExecuteable)) {
|
||||||
|
runFileDetached(userOsuPath, patcherExecuteable);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopOsuStatus() {
|
||||||
|
clearInterval(osuCheckInterval);
|
||||||
|
}
|
||||||
|
|
||||||
function registerIPCPipes() {
|
function registerIPCPipes() {
|
||||||
ipcMain.handle("ezpplauncher:login", async (e, args) => {
|
ipcMain.handle("ezpplauncher:login", async (e, args) => {
|
||||||
const timeout = new AbortController();
|
const timeout = new AbortController();
|
||||||
|
@ -43,6 +78,7 @@ function registerIPCPipes() {
|
||||||
config.set("username", args.username);
|
config.set("username", args.username);
|
||||||
config.set("password", args.password);
|
config.set("password", args.password);
|
||||||
}
|
}
|
||||||
|
currentUser = args;
|
||||||
config.remove("guest");
|
config.remove("guest");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -86,6 +122,12 @@ function registerIPCPipes() {
|
||||||
|
|
||||||
if (fetchResult.ok) {
|
if (fetchResult.ok) {
|
||||||
const result = await fetchResult.json();
|
const result = await fetchResult.json();
|
||||||
|
if ("user" in result) {
|
||||||
|
currentUser = {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -104,12 +146,14 @@ function registerIPCPipes() {
|
||||||
config.remove("username");
|
config.remove("username");
|
||||||
config.remove("password");
|
config.remove("password");
|
||||||
config.set("guest", "1");
|
config.set("guest", "1");
|
||||||
|
currentUser = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("ezpplauncher:logout", (e) => {
|
ipcMain.handle("ezpplauncher:logout", (e) => {
|
||||||
config.remove("username");
|
config.remove("username");
|
||||||
config.remove("password");
|
config.remove("password");
|
||||||
config.remove("guest");
|
config.remove("guest");
|
||||||
|
currentUser = undefined
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -140,12 +184,22 @@ function registerIPCPipes() {
|
||||||
return config.all();
|
return config.all();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("ezpplauncher:launch", async (e) => {
|
ipcMain.handle("ezpplauncher:launch", async (e, args) => {
|
||||||
|
const patch = args.patch;
|
||||||
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
status: "Checking osu! directory...",
|
status: "Checking osu! directory...",
|
||||||
});
|
});
|
||||||
await new Promise((res) => setTimeout(res, 1000));
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
const osuPath = config.get("osuPath");
|
const osuPath = config.get("osuPath");
|
||||||
|
userOsuPath = osuPath;
|
||||||
|
if (osuPath == undefined) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
type: "error",
|
||||||
|
message: "osu! path not set!",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!(await isValidOsuFolder(osuPath))) {
|
if (!(await isValidOsuFolder(osuPath))) {
|
||||||
mainWindow.webContents.send("ezpplauncher:launchabort");
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
mainWindow.webContents.send("ezpplauncher:alert", {
|
mainWindow.webContents.send("ezpplauncher:alert", {
|
||||||
|
@ -158,6 +212,114 @@ function registerIPCPipes() {
|
||||||
status: "Checking for osu! updates...",
|
status: "Checking for osu! updates...",
|
||||||
});
|
});
|
||||||
await new Promise((res) => setTimeout(res, 1000));
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
const releaseStream = await getGlobalConfig(osuPath).get("_ReleaseStream");
|
||||||
|
const latestFiles = await getUpdateFiles(releaseStream);
|
||||||
|
const uiFiles = await getUIFiles(osuPath);
|
||||||
|
const updateFiles = await getFilesThatNeedUpdate(osuPath, latestFiles);
|
||||||
|
if (uiFiles.length > 0) {
|
||||||
|
const uiDownloader = downloadUIFiles(osuPath, uiFiles);
|
||||||
|
uiDownloader.eventEmitter.on("data", (data) => {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: Math.ceil(data.progress),
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total)})...`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await uiDownloader.startDownload();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: -1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (updateFiles.length > 0) {
|
||||||
|
const updateDownloader = downloadUpdateFiles(osuPath, updateFiles);
|
||||||
|
updateDownloader.eventEmitter.on("data", (data) => {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: Math.ceil(data.progress),
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total)})...`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await updateDownloader.startDownload();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: -1,
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "osu! is now up to date!",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
} else {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "osu! is up to date!",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patch) {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Looking for patcher updates...",
|
||||||
|
});
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
const patchFiles = await getPatcherUpdates(osuPath);
|
||||||
|
if (patchFiles.length > 0) {
|
||||||
|
const patcherDownloader = downloadPatcherUpdates(osuPath, patchFiles);
|
||||||
|
patcherDownloader.eventEmitter.on("data", (data) => {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: Math.ceil(data.progress),
|
||||||
|
});
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: `Downloading ${data.fileName}(${formatBytes(data.loaded)}/${formatBytes(data.total)})...`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await patcherDownloader.startDownload();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
|
progress: -1,
|
||||||
|
})
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Patcher is now up to date!",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Patcher is up to date!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await new Promise((res) => setTimeout(res, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Preparing launch...",
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: save credentials to osu!.%username%.cfg
|
||||||
|
if (currentUser) {
|
||||||
|
const userConfig = getUserConfig(osuPath);
|
||||||
|
await userConfig.set("Username", currentUser.username);
|
||||||
|
await userConfig.set("Password", currentUser.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Launching osu!...",
|
||||||
|
});
|
||||||
|
|
||||||
|
const onExitHook = () => {
|
||||||
|
mainWindow.show();
|
||||||
|
stopOsuStatus();
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchstatus", {
|
||||||
|
status: "Waiting for cleanup...",
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
await replaceUIFile(osuPath, true);
|
||||||
|
mainWindow.webContents.send("ezpplauncher:launchabort");
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
await replaceUIFile(osuPath, false);
|
||||||
|
runOsuWithDevServer(osuPath, "ez-pp.farm", onExitHook);
|
||||||
|
mainWindow.hide();
|
||||||
|
startOsuStatus();
|
||||||
|
|
||||||
|
|
||||||
/* mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
/* mainWindow.webContents.send("ezpplauncher:launchprogress", {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
});
|
});
|
||||||
|
|
93
package-lock.json
generated
93
package-lock.json
generated
|
@ -12,8 +12,10 @@
|
||||||
"@types/better-sqlite3": "^7.6.8",
|
"@types/better-sqlite3": "^7.6.8",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
"better-sqlite3": "^9.2.2",
|
"better-sqlite3": "^9.2.2",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
"custom-electron-titlebar": "^4.2.7",
|
"custom-electron-titlebar": "^4.2.7",
|
||||||
"electron-serve": "^1.1.0",
|
"electron-serve": "^1.1.0",
|
||||||
|
"get-window-by-name": "^2.0.0",
|
||||||
"svelte-french-toast": "^1.2.0"
|
"svelte-french-toast": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -3049,6 +3051,12 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
|
||||||
|
"deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in."
|
||||||
|
},
|
||||||
"node_modules/css-declaration-sorter": {
|
"node_modules/css-declaration-sorter": {
|
||||||
"version": "6.4.1",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
|
||||||
|
@ -4291,6 +4299,78 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-window-by-name": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-window-by-name/-/get-window-by-name-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-VXszlUFwkmWAZzxEERRJisiVvGMeB+Zjl5I9f0mwJjhfLTOkD5n5OR9Z518XBZemKLRzIs91TlfKbPmZywS84Q==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"bindings": "^1.2.1",
|
||||||
|
"cross-spawn": "^6.0.5",
|
||||||
|
"nan": "^2.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-window-by-name/node_modules/cross-spawn": {
|
||||||
|
"version": "6.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||||
|
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"nice-try": "^1.0.4",
|
||||||
|
"path-key": "^2.0.1",
|
||||||
|
"semver": "^5.5.0",
|
||||||
|
"shebang-command": "^1.2.0",
|
||||||
|
"which": "^1.2.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-window-by-name/node_modules/path-key": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-window-by-name/node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-window-by-name/node_modules/shebang-command": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
|
||||||
|
"dependencies": {
|
||||||
|
"shebang-regex": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-window-by-name/node_modules/shebang-regex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-window-by-name/node_modules/which": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"which": "bin/which"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/github-from-package": {
|
"node_modules/github-from-package": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||||
|
@ -4905,8 +4985,7 @@
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/jackspeak": {
|
"node_modules/jackspeak": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
|
@ -5553,6 +5632,11 @@
|
||||||
"thenify-all": "^1.0.0"
|
"thenify-all": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nan": {
|
||||||
|
"version": "2.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
||||||
|
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
|
@ -5585,6 +5669,11 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nice-try": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
|
||||||
|
},
|
||||||
"node_modules/node-abi": {
|
"node_modules/node-abi": {
|
||||||
"version": "3.54.0",
|
"version": "3.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz",
|
||||||
|
|
|
@ -37,8 +37,10 @@
|
||||||
"@types/better-sqlite3": "^7.6.8",
|
"@types/better-sqlite3": "^7.6.8",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
"better-sqlite3": "^9.2.2",
|
"better-sqlite3": "^9.2.2",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
"custom-electron-titlebar": "^4.2.7",
|
"custom-electron-titlebar": "^4.2.7",
|
||||||
"electron-serve": "^1.1.0",
|
"electron-serve": "^1.1.0",
|
||||||
|
"get-window-by-name": "^2.0.0",
|
||||||
"svelte-french-toast": "^1.2.0"
|
"svelte-french-toast": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -37,8 +37,8 @@ window.addEventListener("guest-login", async () => {
|
||||||
await ipcRenderer.invoke("ezpplauncher:guestlogin");
|
await ipcRenderer.invoke("ezpplauncher:guestlogin");
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("launch", async () => {
|
window.addEventListener("launch", async (e) => {
|
||||||
await ipcRenderer.invoke("ezpplauncher:launch");
|
await ipcRenderer.invoke("ezpplauncher:launch", e.detail);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("settings-get", async () => {
|
window.addEventListener("settings-get", async () => {
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
launchPercentage.set(progress);
|
launchPercentage.set(progress);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("launchabort", () => {
|
window.addEventListener("launch-abort", () => {
|
||||||
launchPercentage.set(-1);
|
launchPercentage.set(-1);
|
||||||
launchStatus.set("");
|
launchStatus.set("");
|
||||||
launching.set(false);
|
launching.set(false);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
const launch = () => {
|
const launch = () => {
|
||||||
launching.set(true);
|
launching.set(true);
|
||||||
window.dispatchEvent(new CustomEvent("launch"));
|
window.dispatchEvent(new CustomEvent("launch", { detail: { patch: $patch } }));;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -42,11 +42,10 @@
|
||||||
>
|
>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<div class="flex flex-row justify-center items-center gap-5">
|
<div class="flex flex-row justify-center items-center gap-5">
|
||||||
<Button color="light" class="dark:active:!bg-gray-900">Save</Button>
|
|
||||||
<Button
|
<Button
|
||||||
color="red"
|
color="light"
|
||||||
class="dark:active:!bg-red-900 border-red-400"
|
class="dark:active:!bg-gray-900"
|
||||||
on:click={() => currentPage.set(Page.Launch)}>Cancel</Button
|
on:click={() => currentPage.set(Page.Launch)}>Go Back</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
20
src/util/executeUtil.js
Normal file
20
src/util/executeUtil.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
const childProcess = require("child_process");
|
||||||
|
|
||||||
|
const runFile = (folder, file, args, onExit) => {
|
||||||
|
childProcess.execFile(file, args, {
|
||||||
|
cwd: folder
|
||||||
|
}, (_err, _stdout, _stdin) => {
|
||||||
|
if (onExit) onExit();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const runFileDetached = (folder, file, args) => {
|
||||||
|
const subProcess = childProcess.spawn(file + (args ? " " + args : ''), {
|
||||||
|
cwd: folder,
|
||||||
|
detached: true,
|
||||||
|
stdio: 'ignore'
|
||||||
|
});
|
||||||
|
subProcess.unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { runFile, runFileDetached };
|
13
src/util/formattingUtil.js
Normal file
13
src/util/formattingUtil.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
function formatBytes(bytes, decimals = 2) {
|
||||||
|
if (!+bytes) return '0 Bytes'
|
||||||
|
|
||||||
|
const k = 1024
|
||||||
|
const dm = decimals < 0 ? 0 : decimals
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||||
|
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
|
||||||
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { formatBytes };
|
|
@ -1,5 +1,9 @@
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
const EventEmitter = require("events");
|
||||||
|
const { default: axios } = require("axios");
|
||||||
|
const { runFile } = require("./executeUtil");
|
||||||
|
|
||||||
const checkUpdateURL =
|
const checkUpdateURL =
|
||||||
"https://osu.ppy.sh/web/check-updates.php?action=check&stream=";
|
"https://osu.ppy.sh/web/check-updates.php?action=check&stream=";
|
||||||
|
@ -30,6 +34,27 @@ const osuEntities = [
|
||||||
"scores.db",
|
"scores.db",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const patcherFiles = [
|
||||||
|
{
|
||||||
|
name: "patcher.exe",
|
||||||
|
url_download: "https://ez-pp.farm/assets/patcher.exe",
|
||||||
|
url_hash: "https://ez-pp.farm/assets/patcher.md5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "patch.hook.dll",
|
||||||
|
url_download: "https://ez-pp.farm/assets/patch.hook.dll",
|
||||||
|
url_hash: "https://ez-pp.farm/assets/patch.hook.md5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const uiFiles = [
|
||||||
|
{
|
||||||
|
name: "ezpp!ui.dll",
|
||||||
|
url_download: "https://ez-pp.farm/assets/ezpp!ui.dll",
|
||||||
|
url_hash: "https://ez-pp.farm/assets/ezpp!ui.md5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
async function isValidOsuFolder(path) {
|
async function isValidOsuFolder(path) {
|
||||||
const allFiles = await fs.promises.readdir(path);
|
const allFiles = await fs.promises.readdir(path);
|
||||||
let matches = 0;
|
let matches = 0;
|
||||||
|
@ -39,10 +64,71 @@ async function isValidOsuFolder(path) {
|
||||||
return (Math.round((matches / osuEntities.length) * 100) >= 60);
|
return (Math.round((matches / osuEntities.length) * 100) >= 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserConfig(osuPath) {
|
function getGlobalConfig(osuPath) {
|
||||||
const configFileInfo = {
|
const configFileInfo = {
|
||||||
name: "",
|
name: "",
|
||||||
path: "",
|
path: "",
|
||||||
|
get: async (key) => {
|
||||||
|
if (!configFileInfo.path) {
|
||||||
|
console.log("config file not loaded");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fileStream = await fs.promises.readFile(
|
||||||
|
configFileInfo.path,
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
const lines = fileStream.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(" = ")) {
|
||||||
|
const argsPair = line.split(" = ", 2);
|
||||||
|
const keyname = argsPair[0];
|
||||||
|
const value = argsPair[1];
|
||||||
|
if (keyname == key) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const globalOsuConfig = path.join(osuPath, "osu!.cfg");
|
||||||
|
if (fs.existsSync(globalOsuConfig)) {
|
||||||
|
configFileInfo.name = "osu!.cfg";
|
||||||
|
configFileInfo.path = globalOsuConfig;
|
||||||
|
}
|
||||||
|
return configFileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserConfig(osuPath) {
|
||||||
|
const configFileInfo = {
|
||||||
|
name: "",
|
||||||
|
path: "",
|
||||||
|
set: async (key, newValue) => {
|
||||||
|
if (!configFileInfo.path) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fileContents = [];
|
||||||
|
const fileStream = await fs.promises.readFile(
|
||||||
|
configFileInfo.path,
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
const lines = fileStream.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(" = ")) {
|
||||||
|
const argsPair = line.split(" = ", 2);
|
||||||
|
const keyname = argsPair[0];
|
||||||
|
if (keyname == key) {
|
||||||
|
fileContents.push(`${key} = ${newValue}`);
|
||||||
|
} else {
|
||||||
|
fileContents.push(line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileContents.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await fs.promises.writeFile(configFileInfo.path, fileContents.join("\n"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
get: async (key) => {
|
get: async (key) => {
|
||||||
if (!configFileInfo.path) {
|
if (!configFileInfo.path) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -80,4 +166,194 @@ async function getUpdateFiles(releaseStream) {
|
||||||
return releaseData.ok ? await releaseData.json() : undefined;
|
return releaseData.ok ? await releaseData.json() : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { isValidOsuFolder, getUserConfig, getUpdateFiles };
|
async function getFilesThatNeedUpdate(osuPath, releaseStreamFiles) {
|
||||||
|
const updateFiles = [];
|
||||||
|
for (const updatePatch of releaseStreamFiles) {
|
||||||
|
const fileName = updatePatch.filename;
|
||||||
|
const fileHash = updatePatch.file_hash;
|
||||||
|
|
||||||
|
const fileOnDisk = path.join(osuPath, fileName);
|
||||||
|
if (fs.existsSync(fileOnDisk)) {
|
||||||
|
if (ignoredOsuEntities.includes(fileName)) continue;
|
||||||
|
const fileHashOnDisk = crypto.createHash("md5").update(fs.readFileSync(fileOnDisk)).digest("hex");
|
||||||
|
if (fileHashOnDisk.trim().toLowerCase() != fileHash.trim().toLowerCase()) {
|
||||||
|
console.log({
|
||||||
|
fileOnDisk,
|
||||||
|
fileHashOnDisk,
|
||||||
|
fileHash
|
||||||
|
})
|
||||||
|
updateFiles.push(updatePatch);
|
||||||
|
}
|
||||||
|
} else updateFiles.push(updatePatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadUpdateFiles(osuPath, updateFiles) {
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
const startDownload = async () => {
|
||||||
|
for (const updatePatch of updateFiles) {
|
||||||
|
const fileName = updatePatch.filename;
|
||||||
|
const fileSize = updatePatch.filesize;
|
||||||
|
const fileURL = updatePatch.url_full;
|
||||||
|
|
||||||
|
const axiosDownloadWithProgress = await axios.get(fileURL, {
|
||||||
|
responseType: "stream",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const { loaded, total } = progressEvent;
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: Math.floor((loaded / total) * 100)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
axiosDownloadWithProgress.data.on("end", () => {
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded: fileSize,
|
||||||
|
total: fileSize,
|
||||||
|
progress: 100
|
||||||
|
});
|
||||||
|
})
|
||||||
|
await fs.promises.writeFile(path.join(osuPath, fileName), axiosDownloadWithProgress.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait until all files are downloaded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventEmitter,
|
||||||
|
startDownload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runOsuWithDevServer(osuPath, serverDomain, onExit) {
|
||||||
|
const osuExecuteable = path.join(osuPath, "osu!.exe");
|
||||||
|
runFile(osuPath, osuExecuteable, ["-devserver", serverDomain], onExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPatcherUpdates(osuPath) {
|
||||||
|
|
||||||
|
const filesToDownload = [];
|
||||||
|
|
||||||
|
const patcherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(patcherDir)) fs.mkdirSync(patcherDir);
|
||||||
|
|
||||||
|
for (const patcherFile of patcherFiles) {
|
||||||
|
if (fs.existsSync(path.join(patcherDir, patcherFile.name))) {
|
||||||
|
const latestPatchFileHash = await (await fetch(patcherFile.url_hash)).text();
|
||||||
|
const localPatchFileHash = crypto.createHash("md5").update(fs.readFileSync(path.join(patcherDir, patcherFile.name))).digest("hex");
|
||||||
|
if (latestPatchFileHash.trim().toLowerCase() != localPatchFileHash.trim().toLowerCase()) filesToDownload.push(patcherFile);
|
||||||
|
} else filesToDownload.push(patcherFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesToDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadPatcherUpdates(osuPath, patcherUpdates) {
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
const startDownload = async () => {
|
||||||
|
|
||||||
|
const patcherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(patcherDir)) fs.mkdirSync(patcherDir);
|
||||||
|
|
||||||
|
for (const patcherFile of patcherUpdates) {
|
||||||
|
const fileName = patcherFile.name;
|
||||||
|
const fileURL = patcherFile.url_download;
|
||||||
|
const axiosDownloadWithProgress = await axios.get(fileURL, {
|
||||||
|
responseType: "stream",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const { loaded, total } = progressEvent;
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: Math.floor((loaded / total) * 100)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.promises.writeFile(path.join(osuPath, "EZPPLauncher", fileName), axiosDownloadWithProgress.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventEmitter,
|
||||||
|
startDownload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUIFiles(osuPath) {
|
||||||
|
|
||||||
|
const filesToDownload = [];
|
||||||
|
|
||||||
|
const ezppLauncherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(ezppLauncherDir)) fs.mkdirSync(ezppLauncherDir);
|
||||||
|
|
||||||
|
for (const uiFile of uiFiles) {
|
||||||
|
if (fs.existsSync(path.join(ezppLauncherDir, uiFile.name))) {
|
||||||
|
const latestPatchFileHash = await (await fetch(uiFile.url_hash)).text();
|
||||||
|
const localPatchFileHash = crypto.createHash("md5").update(fs.readFileSync(path.join(ezppLauncherDir, uiFile.name))).digest("hex");
|
||||||
|
if (latestPatchFileHash.trim().toLowerCase() != localPatchFileHash.trim().toLowerCase()) filesToDownload.push(uiFile);
|
||||||
|
} else filesToDownload.push(uiFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesToDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadUIFiles(osuPath, uiFiles) {
|
||||||
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
const startDownload = async () => {
|
||||||
|
|
||||||
|
const ezpplauncherDir = path.join(osuPath, "EZPPLauncher");
|
||||||
|
if (!fs.existsSync(ezpplauncherDir)) fs.mkdirSync(ezpplauncherDir);
|
||||||
|
|
||||||
|
for (const uiFile of uiFiles) {
|
||||||
|
const fileName = uiFile.name;
|
||||||
|
const fileURL = uiFile.url_download;
|
||||||
|
const axiosDownloadWithProgress = await axios.get(fileURL, {
|
||||||
|
responseType: "stream",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
const { loaded, total } = progressEvent;
|
||||||
|
eventEmitter.emit("data", {
|
||||||
|
fileName,
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress: Math.floor((loaded / total) * 100)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.promises.writeFile(path.join(osuPath, "EZPPLauncher", fileName), axiosDownloadWithProgress.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventEmitter,
|
||||||
|
startDownload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function replaceUIFile(osuPath, revert) {
|
||||||
|
if (!revert) {
|
||||||
|
const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll");
|
||||||
|
const oldOsuUIFile = path.join(osuPath, "osu!ui.dll");
|
||||||
|
await fs.promises.rename(oldOsuUIFile, path.join(osuPath, "osu!ui.dll.bak"));
|
||||||
|
await fs.promises.rename(ezppUIFile, oldOsuUIFile);
|
||||||
|
} else {
|
||||||
|
const oldOsuUIFile = path.join(osuPath, "osu!ui.dll");
|
||||||
|
const ezppUIFile = path.join(osuPath, "EZPPLauncher", "ezpp!ui.dll");
|
||||||
|
await fs.promises.rename(oldOsuUIFile, ezppUIFile);
|
||||||
|
await fs.promises.rename(path.join(osuPath, "osu!ui.dll.bak"), oldOsuUIFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { isValidOsuFolder, getUserConfig, getGlobalConfig, getUpdateFiles, getFilesThatNeedUpdate, downloadUpdateFiles, runOsuWithDevServer, getPatcherUpdates, downloadPatcherUpdates, downloadUIFiles, getUIFiles, replaceUIFile };
|
||||||
|
|
14
tests/fileHash.js
Normal file
14
tests/fileHash.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const fs = require("fs");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const correctHash = 'b66478cc0f9ec50810489a039ced642b';
|
||||||
|
const filePath = 'C:\\Users\\horiz\\AppData\\Local\\osu!\\avcodec-51.dll';
|
||||||
|
const fileHash = crypto.createHash('md5').update(await fs.promises.readFile(filePath)).digest('hex');
|
||||||
|
|
||||||
|
console.log({
|
||||||
|
correctHash,
|
||||||
|
fileHash,
|
||||||
|
matching: correctHash === fileHash,
|
||||||
|
})
|
||||||
|
})();
|
9
tests/osuConfig.js
Normal file
9
tests/osuConfig.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
const { getGlobalConfig } = require("../src/util/osuUtil");
|
||||||
|
const config = require("../src/config/config");
|
||||||
|
(async () => {
|
||||||
|
const osuPath = config.get("osuPath");
|
||||||
|
const globalConfig = getGlobalConfig(osuPath);
|
||||||
|
|
||||||
|
const globalConfigContent = await globalConfig.get("_ReleaseStream");
|
||||||
|
console.log(globalConfigContent);
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user