mirror of
https://github.com/MrLowPr/overdrive.git
synced 2026-10-18 21:01:17 +02:00
106 lines
4.2 KiB
JavaScript
106 lines
4.2 KiB
JavaScript
// ../pub-game-store/native/files/useFiles.js
|
|
// reconstructed from the esbuild bundle in eboot.bin
|
|
|
|
import { useEffect, useMemo, useState } from 'react';
|
|
import { deletionTargets, scanDrive } from './scan.js';
|
|
import { getGame } from '../state/catalog.js';
|
|
import { COMPLETE } from '../state/download-queue.js';
|
|
import { downloadQueue, shadowMountUp } from '../state/downloads.js';
|
|
import { mountedImages, shadowRequest } from '../state/shadowmount.js';
|
|
import { FileSystem, Http, Packages, Platform } from '@ps5-react/core';
|
|
|
|
function installedTitles() {
|
|
try {
|
|
return new Set(FileSystem.readDir("/user/app").map((entry2) => entry2.name));
|
|
} catch {
|
|
return /* @__PURE__ */ new Set();
|
|
}
|
|
}
|
|
var owned = (job2) => [job2.artifact?.destination, job2.downloadDestination, job2.output, ...job2.packages ?? []];
|
|
function finishedJobs(path) {
|
|
try {
|
|
const queue = downloadQueue();
|
|
return { queue, jobs: queue.snapshot().filter((job2) => COMPLETE.includes(job2.state) && owned(job2).includes(path)) };
|
|
} catch {
|
|
return { queue: null, jobs: [] };
|
|
}
|
|
}
|
|
async function checkUnmounted(file, root2) {
|
|
if (Platform.OS !== "ps5" || file.kind !== "images") return;
|
|
let mounted;
|
|
try {
|
|
mounted = await mountedImages((route, body) => shadowRequest(Http, route, body), deletionTargets(file), root2);
|
|
} catch (error2) {
|
|
throw new Error(`ShadowMount could not say whether it is mounted, so it was kept: ${error2.message}`);
|
|
}
|
|
console.log(`[files] ${file.path}: ${mounted === null ? "ShadowMount is not running, so it is not mounted" : mounted.length ? `still mounted (${mounted.map((image) => image.path).join(", ")})` : "not mounted"}`);
|
|
if (mounted?.length) throw new Error("ShadowMount still has it mounted, so it was kept. Close any game and try again.");
|
|
}
|
|
function useFiles(root2) {
|
|
const [version2, setVersion] = (0, useState)(0);
|
|
const rescan = () => setVersion((value) => value + 1);
|
|
const files = (0, useMemo)(() => {
|
|
const installed = installedTitles();
|
|
return scanDrive(FileSystem, root2).map((file) => ({
|
|
...file,
|
|
game: file.titleId ? getGame(file.titleId) ?? null : null,
|
|
installed: Boolean(file.titleId && installed.has(file.titleId))
|
|
}));
|
|
}, [root2, version2]);
|
|
const [installing, setInstalling] = (0, useState)(null);
|
|
const [shadowMount, setShadowMount] = (0, useState)(true);
|
|
(0, useEffect)(() => {
|
|
let live = true;
|
|
void shadowMountUp().then((up) => {
|
|
if (live) setShadowMount(up);
|
|
});
|
|
return () => {
|
|
live = false;
|
|
};
|
|
}, [version2]);
|
|
const install = (file) => {
|
|
if (installing && !installing.done && !installing.error) return;
|
|
let task;
|
|
try {
|
|
task = Packages.install({ path: file.path, name: file.game?.title ?? file.name });
|
|
} catch (error2) {
|
|
setInstalling({ path: file.path, fraction: 0, error: error2.message });
|
|
return;
|
|
}
|
|
setInstalling({ path: file.path, fraction: 0 });
|
|
task.subscribe((progress) => setInstalling((state) => state?.path === file.path ? { ...state, fraction: progress.total ? progress.written / progress.total : 0 } : state));
|
|
task.done.then(
|
|
() => {
|
|
setInstalling((state) => ({ ...state, fraction: 1, done: true }));
|
|
rescan();
|
|
},
|
|
(error2) => setInstalling((state) => ({ ...state, error: error2.message }))
|
|
);
|
|
};
|
|
const remove = async (file) => {
|
|
const { queue, jobs } = finishedJobs(file.path);
|
|
const image = jobs.find((job2) => job2.artifact.nextAction === "shadowmount");
|
|
if (image) {
|
|
await queue.deleteFromDisk(image.id);
|
|
rescan();
|
|
return;
|
|
}
|
|
await checkUnmounted(file, root2);
|
|
for (const path of deletionTargets(file)) {
|
|
if (!FileSystem.exists(path)) continue;
|
|
try {
|
|
await FileSystem.removeAsync(path);
|
|
} catch (error2) {
|
|
console.log(`[files] ${path} not deleted: ${error2.message}`);
|
|
throw error2;
|
|
}
|
|
console.log(`[files] deleted ${path}`);
|
|
}
|
|
for (const job2 of jobs) queue.remove(job2.id);
|
|
rescan();
|
|
};
|
|
return { files, installing, install, remove, rescan, shadowMount };
|
|
}
|
|
|
|
export { useFiles };
|