From f22547b9a463a466e27a4ed0b9f075d7373d00fa Mon Sep 17 00:00:00 2001 From: Ethan Green Date: Wed, 17 Apr 2024 02:13:28 -0400 Subject: [PATCH] Add Balatro + Love2D support via lovely-injector (#1267) lovely-injector is "JIT-like" lua injector for Love2D games: https://github.com/ethangreen-dev/lovely-injector. It injects lua into the game process at runtime, bypassing the need for manual executable patching. --------- Co-authored-by: Oksamies --- src/assets/images/game_selection/Balatro.png | Bin 0 -> 50827 bytes src/installers/LovelyInstaller.ts | 80 ++++++++++++++++++ src/installers/registry.ts | 3 + src/model/game/GameManager.ts | 7 ++ src/model/installing/PackageLoader.ts | 3 + .../PlatformInterceptorImpl.ts | 1 + .../ModLoaderVariantRecord.ts | 2 + .../instructions/GameInstructions.ts | 4 +- .../loader/LovelyGameInstructions.ts | 17 ++++ src/r2mm/manager/SettingsDexieStore.ts | 2 +- 10 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 src/assets/images/game_selection/Balatro.png create mode 100644 src/installers/LovelyInstaller.ts create mode 100644 src/r2mm/launching/instructions/instructions/loader/LovelyGameInstructions.ts diff --git a/src/assets/images/game_selection/Balatro.png b/src/assets/images/game_selection/Balatro.png new file mode 100644 index 0000000000000000000000000000000000000000..f00e53422910dfce59dc697bec2d44cd14d4d3f9 GIT binary patch literal 50827 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 literal 0 HcmV?d00001 diff --git a/src/installers/LovelyInstaller.ts b/src/installers/LovelyInstaller.ts new file mode 100644 index 000000000..9072c7102 --- /dev/null +++ b/src/installers/LovelyInstaller.ts @@ -0,0 +1,80 @@ +import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallRuleInstaller, addToStateFile } from "./InstallRuleInstaller"; +import FsProvider from "../providers/generic/file/FsProvider"; +import FileUtils from "../utils/FileUtils"; +import FileTree from "../model/file/FileTree"; +import R2Error from "../model/errors/R2Error"; +import path from "path"; + +export class LovelyInstaller extends PackageInstaller { + async install(args: InstallArgs) { + const { + mod, + packagePath, + profile, + } = args; + + const profilePath = profile.getPathOfProfile(); + const fs = FsProvider.instance; + const fileRelocations = new Map(); + + // Manually copy over version.dll + const dwmSrc = path.join(packagePath, "version.dll"); + const dwmDest = path.join(profilePath, "version.dll"); + await fs.copyFile(dwmSrc, dwmDest); + fileRelocations.set(dwmSrc, "version.dll"); + + // Files within the lovely subdirectory need to be recursively copied into the destination. + const lovelyTree = await FileTree.buildFromLocation(path.join(packagePath, "lovely")); + if (lovelyTree instanceof R2Error) { + throw lovelyTree; + } + + const targets = lovelyTree.getRecursiveFiles().map((x) => x.replace(packagePath, "")).map((x) => [x, path.join("mods", x)]); + for (const target of targets) { + const absSrc = path.join(packagePath, target[0]); + const absDest = path.join(profilePath, target[1]); + + await FileUtils.ensureDirectory(path.dirname(absDest)); + await fs.copyFile(absSrc, absDest); + + fileRelocations.set(absSrc, target[1]); + } + + await addToStateFile(mod, fileRelocations, profile); + } +} + +export class LovelyPluginInstaller extends PackageInstaller { + async install(args: InstallArgs) { + const { + mod, + packagePath, + profile, + } = args; + + const profilePath = profile.getPathOfProfile(); + const installDir = path.join("mods", mod.getName()); + + const fs = FsProvider.instance; + const fileRelocations = new Map(); + + const srcTree = await FileTree.buildFromLocation(packagePath); + if (srcTree instanceof R2Error) { + throw R2Error; + } + + const srcFiles = srcTree.getRecursiveFiles(); + for (const srcFile of srcFiles) { + const relFile = srcFile.replace(packagePath, ""); + const destFile = path.join(profilePath, installDir, relFile); + + await FileUtils.ensureDirectory(path.dirname(destFile)); + await fs.copyFile(srcFile, destFile); + + fileRelocations.set(srcFile, path.join(installDir, relFile)); + } + + await addToStateFile(mod, fileRelocations, profile); + } +} diff --git a/src/installers/registry.ts b/src/installers/registry.ts index 9bebadeff..b8cf5dd53 100644 --- a/src/installers/registry.ts +++ b/src/installers/registry.ts @@ -4,6 +4,7 @@ import { MelonLoaderInstaller } from "./MelonLoaderInstaller"; import { PackageInstaller } from "./PackageInstaller"; import { InstallRuleInstaller } from "./InstallRuleInstaller"; import { ShimloaderInstaller, ShimloaderPluginInstaller } from "./ShimloaderInstaller"; +import { LovelyInstaller, LovelyPluginInstaller } from "./LovelyInstaller"; const _PackageInstallers = { @@ -13,6 +14,8 @@ const _PackageInstallers = { "melonloader": new MelonLoaderInstaller(), "shimloader": new ShimloaderInstaller(), "shimloader-plugin": new ShimloaderPluginInstaller(), + "lovely": new LovelyInstaller(), + "lovely-plugin": new LovelyPluginInstaller(), } export type PackageInstallerId = keyof typeof _PackageInstallers; diff --git a/src/model/game/GameManager.ts b/src/model/game/GameManager.ts index 542ebe747..1235a85c1 100644 --- a/src/model/game/GameManager.ts +++ b/src/model/game/GameManager.ts @@ -569,6 +569,7 @@ export default class GameManager { "https://thunderstore.io/c/sailwind/api/v1/package/", EXCLUSIONS, [new StorePlatformMetadata(StorePlatform.STEAM, "1764530")], "Sailwind.png", GameSelectionDisplayMode.VISIBLE, GameInstanceType.GAME, PackageLoader.BEPINEX, []), + new Game( "Voices of the Void", "VotV", "VotV", "", ["VotV.exe"], "VotV", @@ -594,6 +595,12 @@ export default class GameManager { "https://thunderstore.io/c/content-warning/api/v1/package/", EXCLUSIONS, [new StorePlatformMetadata(StorePlatform.STEAM, "2881650")], "ContentWarning.png", GameSelectionDisplayMode.VISIBLE, GameInstanceType.GAME, PackageLoader.BEPINEX, []), + + new Game("Balatro", "Balatro", "Balatro", + "Balatro", ["Balatro.exe"], "Balatro_Data", + "https://thunderstore.io/c/balatro/api/v1/package/", EXCLUSIONS, + [new StorePlatformMetadata(StorePlatform.STEAM, "2379780")], "Balatro.png", + GameSelectionDisplayMode.VISIBLE, GameInstanceType.GAME, PackageLoader.LOVELY, []), ]; static get activeGame(): Game { diff --git a/src/model/installing/PackageLoader.ts b/src/model/installing/PackageLoader.ts index 589af56b6..338036e74 100644 --- a/src/model/installing/PackageLoader.ts +++ b/src/model/installing/PackageLoader.ts @@ -8,6 +8,7 @@ export enum PackageLoader { GODOT_ML, ANCIENT_DUNGEON_VR, SHIMLOADER, + LOVELY, } export function GetInstallerIdForLoader(loader: PackageLoader): PackageInstallerId | null { @@ -19,6 +20,7 @@ export function GetInstallerIdForLoader(loader: PackageLoader): PackageInstaller case PackageLoader.GODOT_ML: return "godotml"; case PackageLoader.NORTHSTAR: return "bepinex"; case PackageLoader.SHIMLOADER: return "shimloader"; + case PackageLoader.LOVELY: return "lovely"; case PackageLoader.ANCIENT_DUNGEON_VR: return null; } } @@ -26,6 +28,7 @@ export function GetInstallerIdForLoader(loader: PackageLoader): PackageInstaller export function GetInstallerIdForPlugin(loader: PackageLoader): PackageInstallerId | null { switch (loader) { case PackageLoader.SHIMLOADER: return "shimloader-plugin"; + case PackageLoader.LOVELY: return "lovely-plugin"; } return null; diff --git a/src/providers/generic/game/platform_interceptor/PlatformInterceptorImpl.ts b/src/providers/generic/game/platform_interceptor/PlatformInterceptorImpl.ts index ae5d22e55..c965633a5 100644 --- a/src/providers/generic/game/platform_interceptor/PlatformInterceptorImpl.ts +++ b/src/providers/generic/game/platform_interceptor/PlatformInterceptorImpl.ts @@ -64,6 +64,7 @@ function buildRunners(runners: PlatformRunnersType): LoaderRunnersType { [PackageLoader.ANCIENT_DUNGEON_VR]: runners, [PackageLoader.GODOT_ML]: runners, [PackageLoader.SHIMLOADER]: runners, + [PackageLoader.LOVELY]: runners, } } diff --git a/src/r2mm/installing/profile_installers/ModLoaderVariantRecord.ts b/src/r2mm/installing/profile_installers/ModLoaderVariantRecord.ts index e399b3274..d91f63fee 100644 --- a/src/r2mm/installing/profile_installers/ModLoaderVariantRecord.ts +++ b/src/r2mm/installing/profile_installers/ModLoaderVariantRecord.ts @@ -67,6 +67,7 @@ export const MODLOADER_PACKAGES = [ new ModLoaderPackageMapping("BepInEx_Wormtown-BepInExPack", "BepInExPack", PackageLoader.BEPINEX), new ModLoaderPackageMapping("0xFFF7-votv_shimloader", "", PackageLoader.SHIMLOADER), new ModLoaderPackageMapping("Thunderstore-unreal_shimloader", "", PackageLoader.SHIMLOADER), + new ModLoaderPackageMapping("Thunderstore-lovely", "", PackageLoader.LOVELY), ]; @@ -165,6 +166,7 @@ const VARIANTS = { Palworld: MODLOADER_PACKAGES, Plasma: MODLOADER_PACKAGES, ContentWarning: MODLOADER_PACKAGES, + Balatro: MODLOADER_PACKAGES, }; // Exported separately from the definition in order to preserve the key names in the type definition. // Otherwise this would become [key: string] and we couldn't use the game names for type hinting elsewhere. diff --git a/src/r2mm/launching/instructions/GameInstructions.ts b/src/r2mm/launching/instructions/GameInstructions.ts index 4cbe9818f..37368db03 100644 --- a/src/r2mm/launching/instructions/GameInstructions.ts +++ b/src/r2mm/launching/instructions/GameInstructions.ts @@ -8,6 +8,7 @@ import NorthstarGameInstructions from './instructions/loader/NorthstarGameInstru import { GodotMLGameInstructions } from "../../launching/instructions/instructions/loader/GodotMLGameInstructions"; import { AncientVRGameInstructions } from "../../launching/instructions/instructions/loader/AncientVRGameInstructions"; import ShimloaderGameInstructions from './instructions/loader/ShimloaderGameInstructions'; +import LovelyGameInstructions from './instructions/loader/LovelyGameInstructions'; export interface GameInstruction { moddedParameters: string, @@ -23,7 +24,8 @@ export default class GameInstructions { [PackageLoader.NORTHSTAR, new NorthstarGameInstructions()], [PackageLoader.GODOT_ML, new GodotMLGameInstructions()], [PackageLoader.ANCIENT_DUNGEON_VR, new AncientVRGameInstructions()], - [PackageLoader.SHIMLOADER, new ShimloaderGameInstructions()] + [PackageLoader.SHIMLOADER, new ShimloaderGameInstructions()], + [PackageLoader.LOVELY, new LovelyGameInstructions()], ]); public static async getInstructionsForGame(game: Game, profile: Profile): Promise { diff --git a/src/r2mm/launching/instructions/instructions/loader/LovelyGameInstructions.ts b/src/r2mm/launching/instructions/instructions/loader/LovelyGameInstructions.ts new file mode 100644 index 000000000..802ec78bd --- /dev/null +++ b/src/r2mm/launching/instructions/instructions/loader/LovelyGameInstructions.ts @@ -0,0 +1,17 @@ +import GameInstructionGenerator from '../GameInstructionGenerator'; +import { GameInstruction } from '../../GameInstructions'; +import Game from '../../../../../model/game/Game'; +import Profile from '../../../../../model/Profile'; +import * as path from 'path'; + +export default class LovelyGameInstructions extends GameInstructionGenerator { + + public async generate(game: Game, profile: Profile): Promise { + const modDir = path.join(profile.getPathOfProfile(), "mods"); + + return { + moddedParameters: `--mod-dir "${modDir}"`, + vanillaParameters: "--vanilla" + }; + } +} diff --git a/src/r2mm/manager/SettingsDexieStore.ts b/src/r2mm/manager/SettingsDexieStore.ts index 2c871c81f..65d3cafde 100644 --- a/src/r2mm/manager/SettingsDexieStore.ts +++ b/src/r2mm/manager/SettingsDexieStore.ts @@ -34,7 +34,7 @@ export default class SettingsDexieStore extends Dexie { // Add all games to store. Borked v2-3 locally // Increment per game or change to settings. - this.version(68).stores(store); + this.version(69).stores(store); this.activeGame = game; this.global = this.table("value");