mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 13:56:16 +00:00
Merge branch 'main' into feat/HYD-822
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
"classnames": "^2.5.1",
|
||||
"color": "^4.2.3",
|
||||
"color.js": "^1.2.0",
|
||||
"crc": "^4.3.2",
|
||||
"create-desktop-shortcuts": "^1.11.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"dexie": "^4.0.10",
|
||||
@@ -70,10 +71,12 @@
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"sound-play": "^1.1.0",
|
||||
"steam-shortcut-editor": "https://github.com/hydralauncher/steam-shortcut-editor",
|
||||
"sudo-prompt": "^9.2.1",
|
||||
"tar": "^7.4.3",
|
||||
"tough-cookie": "^5.1.1",
|
||||
"user-agents": "^1.1.387",
|
||||
"winreg": "^1.2.5",
|
||||
"ws": "^8.18.1",
|
||||
"yaml": "^2.6.1",
|
||||
"yup": "^1.5.0",
|
||||
@@ -99,6 +102,7 @@
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/sound-play": "^1.1.3",
|
||||
"@types/user-agents": "^1.0.4",
|
||||
"@types/winreg": "^1.2.36",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"electron": "^31.7.7",
|
||||
|
||||
@@ -130,9 +130,11 @@
|
||||
"download_in_progress": "Download in progress",
|
||||
"download_paused": "Download paused",
|
||||
"last_downloaded_option": "Last downloaded option",
|
||||
"create_steam_shortcut": "Create Steam shortcut",
|
||||
"create_shortcut_success": "Shortcut created successfully",
|
||||
"you_might_need_to_restart_steam": "You might need to restart Steam to see the changes",
|
||||
"create_shortcut_error": "Error creating shortcut",
|
||||
"nsfw_content_title": "This game contains innapropriate content",
|
||||
"nsfw_content_title": "This game contains inappropriate content",
|
||||
"nsfw_content_description": "{{title}} contains content that may not be suitable for all ages. Are you sure you want to continue?",
|
||||
"allow_nsfw_content": "Continue",
|
||||
"refuse_nsfw_content": "Go back",
|
||||
|
||||
@@ -130,8 +130,10 @@
|
||||
"danger_zone_section_description": "Eliminar este juego de tu librería o los archivos descargados por Hydra (Esto solo eliminará los archivos de instalación y no el juego instalado)",
|
||||
"download_in_progress": "Descarga en progreso",
|
||||
"download_paused": "Descarga pausada",
|
||||
"create_steam_shortcut": "Crear atajo de Steam",
|
||||
"last_downloaded_option": "Última opción descargada",
|
||||
"create_shortcut_success": "Atajo creado con éxito",
|
||||
"you_might_need_to_restart_steam": "Es posible que necesites reiniciar Steam para ver los cambios",
|
||||
"create_shortcut_error": "Error al crear un atajo",
|
||||
"nsfw_content_title": "Este juego contiene contenido inapropiado.",
|
||||
"nsfw_content_description": "{{title}} puede ser no adecuado para todas las edades por su contenido. \n¿Deseas continuar de igual forma?",
|
||||
|
||||
@@ -118,7 +118,9 @@
|
||||
"download_in_progress": "Download em andamento",
|
||||
"download_paused": "Download pausado",
|
||||
"last_downloaded_option": "Última opção baixada",
|
||||
"create_steam_shortcut": "Criar atalho na Steam",
|
||||
"create_shortcut_success": "Atalho criado com sucesso",
|
||||
"you_might_need_to_restart_steam": "Você pode precisar reiniciar a Steam para ver as alterações",
|
||||
"create_shortcut_error": "Erro ao criar atalho",
|
||||
"nsfw_content_title": "Este jogo contém conteúdo inapropriado",
|
||||
"nsfw_content_description": "{{title}} contém conteúdo que pode não ser apropriado para todas as idades. Você deseja continuar?",
|
||||
|
||||
@@ -40,4 +40,6 @@ export const backupsPath = path.join(SystemPath.getPath("userData"), "Backups");
|
||||
|
||||
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
|
||||
|
||||
export const ASSETS_PATH = path.join(SystemPath.getPath("userData"), "Assets");
|
||||
|
||||
export const MAIN_LOOP_INTERVAL = 1500;
|
||||
|
||||
@@ -35,6 +35,7 @@ import "./library/select-game-wine-prefix";
|
||||
import "./library/reset-game-achievements";
|
||||
import "./library/toggle-automatic-cloud-sync";
|
||||
import "./library/get-default-wine-prefix-selection-path";
|
||||
import "./library/create-steam-shortcut";
|
||||
import "./misc/open-checkout";
|
||||
import "./misc/open-external";
|
||||
import "./misc/show-open-dialog";
|
||||
|
||||
181
src/main/events/library/create-steam-shortcut.ts
Normal file
181
src/main/events/library/create-steam-shortcut.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import type { GameShop, GameStats } from "@types";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import {
|
||||
composeSteamShortcut,
|
||||
getSteamLocation,
|
||||
getSteamShortcuts,
|
||||
getSteamUsersIds,
|
||||
HydraApi,
|
||||
logger,
|
||||
SystemPath,
|
||||
writeSteamShortcuts,
|
||||
} from "@main/services";
|
||||
import fs from "node:fs";
|
||||
import axios from "axios";
|
||||
import path from "node:path";
|
||||
import { ASSETS_PATH } from "@main/constants";
|
||||
|
||||
const downloadAsset = async (downloadPath: string, url?: string | null) => {
|
||||
try {
|
||||
if (fs.existsSync(downloadPath)) {
|
||||
return downloadPath;
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(downloadPath), { recursive: true });
|
||||
|
||||
const response = await axios.get(url, { responseType: "arraybuffer" });
|
||||
fs.writeFileSync(downloadPath, response.data);
|
||||
|
||||
return downloadPath;
|
||||
} catch (error) {
|
||||
logger.error("Failed to download asset", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const downloadAssetsFromSteam = async (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
assets: GameStats["assets"]
|
||||
) => {
|
||||
const gameAssetsPath = path.join(ASSETS_PATH, `${shop}-${objectId}`);
|
||||
|
||||
return await Promise.all([
|
||||
downloadAsset(path.join(gameAssetsPath, "icon.ico"), assets?.iconUrl),
|
||||
downloadAsset(
|
||||
path.join(gameAssetsPath, "hero.jpg"),
|
||||
assets?.libraryHeroImageUrl
|
||||
),
|
||||
downloadAsset(path.join(gameAssetsPath, "logo.png"), assets?.logoImageUrl),
|
||||
downloadAsset(
|
||||
path.join(gameAssetsPath, "cover.jpg"),
|
||||
assets?.coverImageUrl
|
||||
),
|
||||
downloadAsset(
|
||||
path.join(gameAssetsPath, "library.jpg"),
|
||||
assets?.libraryImageUrl
|
||||
),
|
||||
]);
|
||||
};
|
||||
|
||||
const copyAssetIfExists = async (
|
||||
sourcePath: string | null,
|
||||
destinationPath: string
|
||||
) => {
|
||||
if (sourcePath && fs.existsSync(sourcePath)) {
|
||||
logger.info("Copying Steam asset", sourcePath, destinationPath);
|
||||
await fs.promises.cp(sourcePath, destinationPath);
|
||||
}
|
||||
};
|
||||
|
||||
const createSteamShortcut = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
shop: GameShop,
|
||||
objectId: string
|
||||
) => {
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
|
||||
if (game) {
|
||||
if (!game.executablePath) {
|
||||
throw new Error("No executable path found for game");
|
||||
}
|
||||
|
||||
const { assets } = await HydraApi.get<GameStats>(
|
||||
`/games/stats?objectId=${objectId}&shop=${shop}`
|
||||
);
|
||||
|
||||
const steamUserIds = await getSteamUsersIds();
|
||||
|
||||
if (!steamUserIds.length) {
|
||||
logger.error("No Steam user ID found");
|
||||
return;
|
||||
}
|
||||
|
||||
const [iconImage, heroImage, logoImage, coverImage, libraryImage] =
|
||||
await downloadAssetsFromSteam(game.shop, game.objectId, assets);
|
||||
|
||||
const newShortcut = composeSteamShortcut(
|
||||
game.title,
|
||||
game.executablePath,
|
||||
iconImage
|
||||
);
|
||||
|
||||
for (const steamUserId of steamUserIds) {
|
||||
logger.info("Adding shortcut for Steam user", steamUserId);
|
||||
|
||||
const steamShortcuts = await getSteamShortcuts(steamUserId);
|
||||
|
||||
if (steamShortcuts.some((shortcut) => shortcut.appname === game.title)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const gridPath = path.join(
|
||||
await getSteamLocation(),
|
||||
"userdata",
|
||||
steamUserId.toString(),
|
||||
"config",
|
||||
"grid"
|
||||
);
|
||||
|
||||
await fs.promises.mkdir(gridPath, { recursive: true });
|
||||
|
||||
await Promise.all([
|
||||
copyAssetIfExists(
|
||||
heroImage,
|
||||
path.join(gridPath, `${newShortcut.appid}_hero.jpg`)
|
||||
),
|
||||
copyAssetIfExists(
|
||||
logoImage,
|
||||
path.join(gridPath, `${newShortcut.appid}_logo.png`)
|
||||
),
|
||||
copyAssetIfExists(
|
||||
coverImage,
|
||||
path.join(gridPath, `${newShortcut.appid}p.jpg`)
|
||||
),
|
||||
copyAssetIfExists(
|
||||
libraryImage,
|
||||
path.join(gridPath, `${newShortcut.appid}.jpg`)
|
||||
),
|
||||
]);
|
||||
|
||||
steamShortcuts.push(newShortcut);
|
||||
|
||||
logger.info(newShortcut);
|
||||
logger.info("Writing Steam shortcuts", steamShortcuts);
|
||||
|
||||
await writeSteamShortcuts(steamUserId, steamShortcuts);
|
||||
}
|
||||
|
||||
if (process.platform === "linux" && !game.winePrefixPath) {
|
||||
const steamWinePrefixes = path.join(
|
||||
SystemPath.getPath("home"),
|
||||
".local",
|
||||
"share",
|
||||
"Steam",
|
||||
"steamapps",
|
||||
"compatdata"
|
||||
);
|
||||
|
||||
const winePrefixPath = path.join(
|
||||
steamWinePrefixes,
|
||||
newShortcut.appid.toString(),
|
||||
"pfx"
|
||||
);
|
||||
|
||||
await fs.promises.mkdir(winePrefixPath, { recursive: true });
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
winePrefixPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("createSteamShortcut", createSteamShortcut);
|
||||
@@ -16,11 +16,7 @@ const getDefaultWinePrefixSelectionPath = async (
|
||||
"compatdata"
|
||||
);
|
||||
|
||||
if (fs.existsSync(steamWinePrefixes)) {
|
||||
return fs.promises.realpath(steamWinePrefixes);
|
||||
}
|
||||
|
||||
return null;
|
||||
return await fs.promises.realpath(steamWinePrefixes);
|
||||
} catch (err) {
|
||||
logger.error("Failed to get default wine prefix selection path", err);
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ const verifyExecutablePathInUse = async (
|
||||
) => {
|
||||
for await (const game of gamesSublevel.values()) {
|
||||
if (game.executablePath === executablePath) {
|
||||
return true;
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
};
|
||||
|
||||
registerEvent("verifyExecutablePathInUse", verifyExecutablePathInUse);
|
||||
|
||||
@@ -27,7 +27,9 @@ export const loadState = async () => {
|
||||
|
||||
await import("./events");
|
||||
|
||||
Aria2.spawn();
|
||||
if (process.platform !== "darwin") {
|
||||
Aria2.spawn();
|
||||
}
|
||||
|
||||
if (userPreferences?.realDebridApiToken) {
|
||||
RealDebridClient.authorize(userPreferences.realDebridApiToken);
|
||||
|
||||
@@ -22,12 +22,6 @@ const binaryNameByPlatform: Partial<Record<NodeJS.Platform, string>> = {
|
||||
win32: "hydra-python-rpc.exe",
|
||||
};
|
||||
|
||||
const rustBinaryNameByPlatform: Partial<Record<NodeJS.Platform, string>> = {
|
||||
darwin: "hydra-httpdl",
|
||||
linux: "hydra-httpdl",
|
||||
win32: "hydra-httpdl.exe",
|
||||
};
|
||||
|
||||
export class PythonRPC {
|
||||
public static readonly BITTORRENT_PORT = "5881";
|
||||
public static readonly RPC_PORT = "8084";
|
||||
@@ -72,20 +66,6 @@ export class PythonRPC {
|
||||
rpcPassword,
|
||||
initialDownload ? JSON.stringify(initialDownload) : "",
|
||||
initialSeeding ? JSON.stringify(initialSeeding) : "",
|
||||
app.isPackaged
|
||||
? path.join(
|
||||
process.resourcesPath,
|
||||
rustBinaryNameByPlatform[process.platform]!
|
||||
)
|
||||
: path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"rust_rpc",
|
||||
"target",
|
||||
"debug",
|
||||
rustBinaryNameByPlatform[process.platform]!
|
||||
),
|
||||
];
|
||||
|
||||
if (app.isPackaged) {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import axios from "axios";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { crc32 } from "crc";
|
||||
import WinReg from "winreg";
|
||||
import { parseBuffer, writeBuffer } from "steam-shortcut-editor";
|
||||
|
||||
import type { SteamAppDetails } from "@types";
|
||||
import type { SteamAppDetails, SteamShortcut } from "@types";
|
||||
|
||||
import { logger } from "./logger";
|
||||
import { SystemPath } from "./system-path";
|
||||
|
||||
export interface SteamAppDetailsResponse {
|
||||
[key: string]: {
|
||||
@@ -11,6 +17,36 @@ export interface SteamAppDetailsResponse {
|
||||
};
|
||||
}
|
||||
|
||||
export const getSteamLocation = async () => {
|
||||
if (process.platform === "linux") {
|
||||
return path.join(SystemPath.getPath("home"), ".local", "share", "Steam");
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
return path.join(
|
||||
SystemPath.getPath("home"),
|
||||
"Library",
|
||||
"Application Support",
|
||||
"Steam"
|
||||
);
|
||||
}
|
||||
|
||||
const regKey = new WinReg({
|
||||
hive: WinReg.HKCU,
|
||||
key: "\\Software\\Valve\\Steam",
|
||||
});
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
regKey.get("SteamPath", (err, value) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(value.value);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getSteamAppDetails = async (
|
||||
objectId: string,
|
||||
language: string
|
||||
@@ -40,3 +76,86 @@ export const getSteamAppDetails = async (
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
export const getSteamUsersIds = async () => {
|
||||
const userDataPath = await getSteamLocation();
|
||||
|
||||
const userIds = fs.readdirSync(path.join(userDataPath, "userdata"), {
|
||||
withFileTypes: true,
|
||||
});
|
||||
|
||||
return userIds
|
||||
.filter((dir) => dir.isDirectory())
|
||||
.map((dir) => Number(dir.name));
|
||||
};
|
||||
|
||||
export const getSteamShortcuts = async (steamUserId: number) => {
|
||||
const shortcutsPath = path.join(
|
||||
await getSteamLocation(),
|
||||
"userdata",
|
||||
steamUserId.toString(),
|
||||
"config",
|
||||
"shortcuts.vdf"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(shortcutsPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const shortcuts = parseBuffer(fs.readFileSync(shortcutsPath));
|
||||
|
||||
return shortcuts.shortcuts as SteamShortcut[];
|
||||
};
|
||||
|
||||
export const generateSteamShortcutAppId = (
|
||||
exePath: string,
|
||||
gameName: string
|
||||
) => {
|
||||
const input = exePath + gameName;
|
||||
const crcValue = crc32(input) >>> 0;
|
||||
const steamAppId = (crcValue | 0x80000000) >>> 0;
|
||||
return steamAppId;
|
||||
};
|
||||
|
||||
export const composeSteamShortcut = (
|
||||
title: string,
|
||||
executablePath: string,
|
||||
iconPath: string | null
|
||||
): SteamShortcut => {
|
||||
return {
|
||||
appid: generateSteamShortcutAppId(executablePath, title),
|
||||
appname: title,
|
||||
Exe: `"${executablePath}"`,
|
||||
StartDir: `"${path.dirname(executablePath)}"`,
|
||||
icon: iconPath ?? "",
|
||||
ShortcutPath: "",
|
||||
LaunchOptions: "",
|
||||
IsHidden: false,
|
||||
AllowDesktopConfig: true,
|
||||
AllowOverlay: true,
|
||||
OpenVR: false,
|
||||
Devkit: false,
|
||||
DevkitGameID: "",
|
||||
DevkitOverrideAppID: false,
|
||||
LastPlayTime: 0,
|
||||
FlatpakAppID: "",
|
||||
};
|
||||
};
|
||||
|
||||
export const writeSteamShortcuts = async (
|
||||
steamUserId: number,
|
||||
shortcuts: SteamShortcut[]
|
||||
) => {
|
||||
const buffer = writeBuffer({ shortcuts });
|
||||
|
||||
return fs.promises.writeFile(
|
||||
path.join(
|
||||
await getSteamLocation(),
|
||||
"userdata",
|
||||
steamUserId.toString(),
|
||||
"config",
|
||||
"shortcuts.vdf"
|
||||
),
|
||||
buffer
|
||||
);
|
||||
};
|
||||
|
||||
@@ -191,6 +191,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
ipcRenderer.invoke("extractGameDownload", shop, objectId),
|
||||
getDefaultWinePrefixSelectionPath: () =>
|
||||
ipcRenderer.invoke("getDefaultWinePrefixSelectionPath"),
|
||||
createSteamShortcut: (shop: GameShop, objectId: string) =>
|
||||
ipcRenderer.invoke("createSteamShortcut", shop, objectId),
|
||||
onGamesRunning: (
|
||||
cb: (
|
||||
gamesRunning: Pick<GameRunning, "id" | "sessionDurationInMillis">[]
|
||||
|
||||
1
src/renderer/src/declaration.d.ts
vendored
1
src/renderer/src/declaration.d.ts
vendored
@@ -179,6 +179,7 @@ declare global {
|
||||
cb: (shop: GameShop, objectId: string) => void
|
||||
) => () => Electron.IpcRenderer;
|
||||
getDefaultWinePrefixSelectionPath: () => Promise<string | null>;
|
||||
createSteamShortcut: (shop: GameShop, objectId: string) => Promise<void>;
|
||||
|
||||
/* Download sources */
|
||||
putDownloadSource: (
|
||||
|
||||
@@ -8,8 +8,10 @@ import { useDownload, useToast, useUserDetails } from "@renderer/hooks";
|
||||
import { RemoveGameFromLibraryModal } from "./remove-from-library-modal";
|
||||
import { ResetAchievementsModal } from "./reset-achievements-modal";
|
||||
import { FileDirectoryIcon, FileIcon } from "@primer/octicons-react";
|
||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||
import { debounce } from "lodash-es";
|
||||
import "./game-options-modal.scss";
|
||||
import { logger } from "@renderer/logger";
|
||||
|
||||
export interface GameOptionsModalProps {
|
||||
visible: boolean;
|
||||
@@ -45,6 +47,7 @@ export function GameOptionsModal({
|
||||
const [automaticCloudSync, setAutomaticCloudSync] = useState(
|
||||
game.automaticCloudSync ?? false
|
||||
);
|
||||
const [creatingSteamShortcut, setCreatingSteamShortcut] = useState(false);
|
||||
|
||||
const {
|
||||
removeGameInstaller,
|
||||
@@ -107,6 +110,25 @@ export function GameOptionsModal({
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateSteamShortcut = async () => {
|
||||
try {
|
||||
setCreatingSteamShortcut(true);
|
||||
await window.electron.createSteamShortcut(game.shop, game.objectId);
|
||||
|
||||
showSuccessToast(
|
||||
t("create_shortcut_success"),
|
||||
t("you_might_need_to_restart_steam")
|
||||
);
|
||||
|
||||
updateGame();
|
||||
} catch (error: unknown) {
|
||||
logger.error("Failed to create Steam shortcut", error);
|
||||
showErrorToast(t("create_shortcut_error"));
|
||||
} finally {
|
||||
setCreatingSteamShortcut(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateShortcut = async (location: ShortcutLocation) => {
|
||||
window.electron
|
||||
.createGameShortcut(game.shop, game.objectId, location)
|
||||
@@ -142,9 +164,12 @@ export function GameOptionsModal({
|
||||
};
|
||||
|
||||
const handleChangeWinePrefixPath = async () => {
|
||||
const defaultPath =
|
||||
await window.electron.getDefaultWinePrefixSelectionPath();
|
||||
|
||||
const { filePaths } = await window.electron.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
defaultPath: await window.electron.getDefaultWinePrefixSelectionPath(),
|
||||
defaultPath: defaultPath ?? game?.winePrefixPath ?? "",
|
||||
});
|
||||
|
||||
if (filePaths && filePaths.length > 0) {
|
||||
@@ -298,6 +323,14 @@ export function GameOptionsModal({
|
||||
>
|
||||
{t("create_shortcut")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateSteamShortcut}
|
||||
theme="outline"
|
||||
disabled={creatingSteamShortcut}
|
||||
>
|
||||
<SteamLogo />
|
||||
{t("create_steam_shortcut")}
|
||||
</Button>
|
||||
{shouldShowCreateStartMenuShortcut && (
|
||||
<Button
|
||||
onClick={() => handleCreateShortcut("start_menu")}
|
||||
|
||||
@@ -53,3 +53,22 @@ export interface SteamAppDetails {
|
||||
ids: number[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface SteamShortcut {
|
||||
appid: number;
|
||||
appname: string;
|
||||
Exe: string;
|
||||
StartDir: string;
|
||||
icon: string;
|
||||
ShortcutPath: string;
|
||||
LaunchOptions: string;
|
||||
IsHidden: boolean;
|
||||
AllowDesktopConfig: boolean;
|
||||
AllowOverlay: boolean;
|
||||
OpenVR: boolean;
|
||||
Devkit: boolean;
|
||||
DevkitGameID: string;
|
||||
DevkitOverrideAppID: boolean;
|
||||
LastPlayTime: number;
|
||||
FlatpakAppID: string;
|
||||
}
|
||||
|
||||
33
yarn.lock
33
yarn.lock
@@ -3357,6 +3357,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.10.tgz#d5a4b56abac169bfbc8b23d291363a682e6fa087"
|
||||
integrity sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==
|
||||
|
||||
"@types/winreg@^1.2.36":
|
||||
version "1.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@types/winreg/-/winreg-1.2.36.tgz#f1d9a9918cae90a63c6106c98224aca6a36983fc"
|
||||
integrity sha512-DtafHy5A8hbaosXrbr7YdjQZaqVewXmiasRS5J4tYMzt3s1gkh40ixpxgVFfKiQ0JIYetTJABat47v9cpr/sQg==
|
||||
|
||||
"@types/ws@^8.18.1":
|
||||
version "8.18.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9"
|
||||
@@ -4009,7 +4014,7 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||
|
||||
buffer@^5.1.0, buffer@^5.5.0:
|
||||
buffer@^5.1.0, buffer@^5.5.0, buffer@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
@@ -4442,6 +4447,11 @@ crc@^3.8.0:
|
||||
dependencies:
|
||||
buffer "^5.1.0"
|
||||
|
||||
crc@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/crc/-/crc-4.3.2.tgz#49b7821cbf2cf61dfd079ed93863bbebd5469b9a"
|
||||
integrity sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==
|
||||
|
||||
create-desktop-shortcuts@^1.11.1:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/create-desktop-shortcuts/-/create-desktop-shortcuts-1.11.1.tgz#59f9dced7931bda551c0717791a909419472c809"
|
||||
@@ -6914,7 +6924,7 @@ lodash.upperfirst@^4.3.1:
|
||||
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
|
||||
integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
|
||||
|
||||
lodash@^4.17.15:
|
||||
lodash@^4.17.15, lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@@ -7366,6 +7376,13 @@ object-keys@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||
|
||||
object-sizeof@^1.2.0:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/object-sizeof/-/object-sizeof-1.6.3.tgz#6edbbf26825b971fd7a32125a800ed2a9895af95"
|
||||
integrity sha512-LGtilAKuDGKCcvu1Xg3UvAhAeJJlFmblo3faltmOQ80xrGwAHxnauIXucalKdTEksHp/Pq9tZGz1hfyEmjFJPQ==
|
||||
dependencies:
|
||||
buffer "^5.6.0"
|
||||
|
||||
object.assign@^4.1.4, object.assign@^4.1.5:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
|
||||
@@ -8548,6 +8565,13 @@ state-local@^1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5"
|
||||
integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==
|
||||
|
||||
"steam-shortcut-editor@https://github.com/hydralauncher/steam-shortcut-editor":
|
||||
version "3.1.3"
|
||||
resolved "https://github.com/hydralauncher/steam-shortcut-editor#7d9ba44eced1f8840cf6c7d2cd40dd4d3d5c660f"
|
||||
dependencies:
|
||||
lodash "^4.17.21"
|
||||
object-sizeof "^1.2.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
@@ -9443,6 +9467,11 @@ winreg@1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
|
||||
integrity sha512-IHpzORub7kYlb8A43Iig3reOvlcBJGX9gZ0WycHhghHtA65X0LYnMRuJs+aH1abVnMJztQkvQNlltnbPi5aGIA==
|
||||
|
||||
winreg@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.5.tgz#b650383e89278952494b5d113ba049a5a4fa96d8"
|
||||
integrity sha512-uf7tHf+tw0B1y+x+mKTLHkykBgK2KMs3g+KlzmyMbLvICSHQyB/xOFjTT8qZ3oeTFyU7Bbj4FzXitGG6jvKhYw==
|
||||
|
||||
word-wrap@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
||||
|
||||
Reference in New Issue
Block a user