From e8cd849623eeb7051d456574b019ed8537bfee57 Mon Sep 17 00:00:00 2001 From: MathieuG-P <40181755+Zagrios@users.noreply.github.com> Date: Mon, 26 Sep 2022 23:50:17 +0200 Subject: [PATCH] detect installed BSIPA ands mods (partialy) --- package-lock.json | 5 ++ package.json | 1 + src/main/main.ts | 4 ++ .../services/mods/beat-mods-api.service.ts | 20 +++++- .../services/mods/bs-mods-manager.service.ts | 66 ++++++++++++++++++- src/main/services/utils.service.ts | 2 + src/shared/models/mods/mod.interface.ts | 1 + 7 files changed, 93 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42f9c0b1..56ec7e6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9024,6 +9024,11 @@ "escape-string-regexp": "^4.0.0" } }, + "md5-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", + "integrity": "sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==" + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", diff --git a/package.json b/package.json index 307595a3..f31af38c 100644 --- a/package.json +++ b/package.json @@ -232,6 +232,7 @@ "framer-motion": "^7.2.1", "history": "^5.3.0", "is-online": "^9.0.1", + "md5-file": "^5.0.0", "react": "^18.2.0", "react-colorful": "^5.6.1", "react-dom": "^18.2.0", diff --git a/src/main/main.ts b/src/main/main.ts index 8b66731a..88808868 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -14,6 +14,7 @@ import log from 'electron-log'; import './ipcs'; import { UtilsService } from './services/utils.service'; import { WindowManagerService } from './services/window-manager.service'; +import { BsModsManagerService } from './services/mods/bs-mods-manager.service'; export const PRELOAD_PATH = app.isPackaged ? path.join(__dirname, 'preload.js') : path.join(__dirname, '../../.erb/dll/preload.js') @@ -30,6 +31,9 @@ log.catchErrors(); const utilsService = UtilsService.getInstance(); utilsService.setAssetsPath(app.isPackaged ? path.join(process.resourcesPath, 'assets') : path.join(__dirname, '../../assets')); +BsModsManagerService.getInstance().getInstalledMods({BSVersion: "1.18.3"}).then(mods => { + console.log(mods); +}) if (process.env.NODE_ENV === 'production') { const sourceMapSupport = require('source-map-support'); diff --git a/src/main/services/mods/beat-mods-api.service.ts b/src/main/services/mods/beat-mods-api.service.ts index c63bb1ad..12b00584 100644 --- a/src/main/services/mods/beat-mods-api.service.ts +++ b/src/main/services/mods/beat-mods-api.service.ts @@ -13,11 +13,10 @@ export class BeatModsApiService { private readonly BEAT_MODS_API_URL = "https://beatmods.com/api/v1/"; - private readonly versionsCache: BSVersion[] = []; private readonly aliasesCache = new Map(); private readonly versionModsCache = new Map(); - private readonly allModsCache: Mod[] = []; + private allModsCache: Mod[]; public static getInstance(): BeatModsApiService{ if(!BeatModsApiService.instance){ BeatModsApiService.instance = new BeatModsApiService(); } @@ -32,6 +31,10 @@ export class BeatModsApiService { return `${this.BEAT_MODS_API_URL}mod?status=approved&gameVersion=${version.BSVersion}&sort=&sortDirection=1`; } + private getAllModsUrl(): string{ + return `${this.BEAT_MODS_API_URL}mod`; + } + private async getVersionAlias(): Promise>{ if(this.aliasesCache.size){ return this.aliasesCache; } return this.requestService.get>(this.BEAT_MODS_ALIAS).then(rawAliases => { @@ -65,9 +68,20 @@ export class BeatModsApiService { this.versionModsCache.set(version.BSVersion, mods); return mods; }); - } + public async getAllMods(): Promise{ + if(!!this.allModsCache){ return this.allModsCache; } + return this.requestService.get(this.getAllModsUrl()).then(mods => { + this.allModsCache = mods; + return this.allModsCache; + }); + } + + public loadAllMods(){ return this.getAllMods(); } + + + } \ No newline at end of file diff --git a/src/main/services/mods/bs-mods-manager.service.ts b/src/main/services/mods/bs-mods-manager.service.ts index bf86a39f..b6ca219e 100644 --- a/src/main/services/mods/bs-mods-manager.service.ts +++ b/src/main/services/mods/bs-mods-manager.service.ts @@ -1,12 +1,18 @@ import { BSVersion } from "shared/bs-version.interface"; import { Mod } from "shared/models/mods/mod.interface";import { BeatModsApiService } from "./beat-mods-api.service"; -; +import { BSLocalVersionService } from "../bs-local-version.service" +import path from "path"; +import { UtilsService } from "../utils.service"; +import md5File from "md5-file"; +import fs from "fs" export class BsModsManagerService { private static instance: BsModsManagerService; private beatModsApi: BeatModsApiService; + private bsLocalService: BSLocalVersionService; + private utilsService: UtilsService; public static getInstance(): BsModsManagerService{ if(!BsModsManagerService.instance){ BsModsManagerService.instance = new BsModsManagerService(); } @@ -15,14 +21,61 @@ export class BsModsManagerService { private constructor(){ this.beatModsApi = BeatModsApiService.getInstance(); + this.bsLocalService = BSLocalVersionService.getInstance(); + this.utilsService = UtilsService.getInstance(); + } + + private async getModFromHash(hash: string): Promise{ + const allMods = await this.beatModsApi.getAllMods(); + return allMods.find(mod => { + if(mod.name.toLowerCase() === "bsipa" || mod.status === "declined"){ return false; } + return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash)); + }) + } + + private async getIpaFromHash(hash: string): Promise{ + const allMods = await this.beatModsApi.getAllMods(); + return allMods.find(mod => { + if(mod.name.toLowerCase() !== "bsipa"){ return false; } + return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash)); + }) + } + + private async getModsInDir(version: BSVersion, modsDir: ModsInstallFolder): Promise{ + const bsPath = await this.bsLocalService.getVersionPath(version); + const modsPath = path.join(bsPath, modsDir); + if(!this.utilsService.pathExist(modsPath)){ return []; } + const files = fs.readdirSync(modsPath); + const promises = files.map(f => { + console.log() + return (async() => { + const filePath = path.join(modsPath, f) + const ext = path.extname(f); + if(ext !== ".dll" && ext !== ".exe" && ext !== ".manifest"){ return; } + const hash = await md5File(filePath); + return this.getModFromHash(hash); + })() + }); + return Promise.all(promises) + } + + private async getBsipaInstalled(version: BSVersion): Promise{ + const bsPath = await this.bsLocalService.getVersionPath(version); + const injectorPath = path.join(bsPath, "Beat Saber_Data", "Managed", "IPA.Injector.dll"); + if(!this.utilsService.pathExist(injectorPath)){ return undefined; } + const injectorMd5 = await md5File(injectorPath); + return this.getIpaFromHash(injectorMd5); } public getAvailableMods(version: BSVersion): Promise{ return this.beatModsApi.getVersionMods(version); } - public getInstalledMods(version: BSVersion): Promise{ - throw "NOT IMPLEMENTED YET"; + public async getInstalledMods(version: BSVersion): Promise{ + await this.beatModsApi.loadAllMods(); + this.getBsipaInstalled(version).then(bsipa => console.log(bsipa)); + this.getModsInDir(version, ModsInstallFolder.LIBS).then(mods => console.log(mods)); + return []; } public installMods(mods: Mod[], version: BSVersion){ @@ -37,4 +90,11 @@ export class BsModsManagerService { throw "NOT IMPLEMENTED YET"; } +} + +const enum ModsInstallFolder { + PLUGINS = "Plugins", + LIBS = "Libs", + PLUGINS_PENDING = "IPA/Pending/Plugins", + LIBS_PENDING = "IPA/Pending/Libs" } \ No newline at end of file diff --git a/src/main/services/utils.service.ts b/src/main/services/utils.service.ts index dbbc3791..280566a8 100644 --- a/src/main/services/utils.service.ts +++ b/src/main/services/utils.service.ts @@ -75,4 +75,6 @@ export class UtilsService{ } } + + } diff --git a/src/shared/models/mods/mod.interface.ts b/src/shared/models/mods/mod.interface.ts index ffb72137..4a87c50a 100644 --- a/src/shared/models/mods/mod.interface.ts +++ b/src/shared/models/mods/mod.interface.ts @@ -13,6 +13,7 @@ export interface Mod { downloads: DownloadLink[], required: boolean, dependencies: Mod[], + status: string }