securityos/components/apps/Emulator/useEmulator.ts

167 lines
5.0 KiB
TypeScript

import type { Core } from "components/apps/Emulator/config";
import { emulatorCores } from "components/apps/Emulator/config";
import type { Emulator } from "components/apps/Emulator/types";
import useTitle from "components/system/Window/useTitle";
import { useFileSystem } from "contexts/fileSystem";
import { useProcesses } from "contexts/process";
import { basename, dirname, extname, join } from "path";
import { useCallback, useEffect, useRef } from "react";
import { ICON_CACHE, ICON_CACHE_EXTENSION, SAVE_PATH } from "utils/constants";
import { bufferToUrl, loadFiles } from "utils/functions";
import { zipAsync } from "utils/zipFunctions";
const getCore = (extension: string): [string, Core] => {
const lcExt = extension.toLowerCase();
return (Object.entries(emulatorCores).find(([, { ext }]) =>
ext.includes(lcExt)
) || []) as [string, Core];
};
const useEmulator = (
id: string,
url: string,
containerRef: React.MutableRefObject<HTMLDivElement | null>,
setLoading: React.Dispatch<React.SetStateAction<boolean>>,
loading: boolean
): void => {
const { exists, mkdirRecursive, readFile, updateFolder, writeFile } =
useFileSystem();
const { linkElement, processes: { [id]: { closing = false } = {} } = {} } =
useProcesses();
const { prependFileToTitle } = useTitle(id);
const emulatorRef = useRef<Emulator>();
const loadedUrlRef = useRef<string>("");
const loadRom = useCallback(async () => {
if (!url) return;
containerRef.current?.classList.remove("drop");
if (loadedUrlRef.current) {
if (loadedUrlRef.current !== url) {
loadedUrlRef.current = "";
window.EJS_terminate?.();
if (containerRef.current) {
const div = document.createElement("div");
div.id = "emulator";
[...containerRef.current.children].forEach((child) => child.remove());
containerRef.current.append(div);
loadRom();
}
}
return;
}
loadedUrlRef.current = url;
const ext = extname(url);
window.EJS_gameName = basename(url, ext);
const [console, { core = "", zip = false } = {}] = getCore(ext);
const rom = await readFile(url);
window.EJS_gameUrl = bufferToUrl(
zip ? Buffer.from(await zipAsync({ [basename(url)]: rom })) : rom
);
window.EJS_core = core;
const saveName = `${basename(url)}.sav`;
const savePath = join(SAVE_PATH, saveName);
window.EJS_onGameStart = ({ detail: { emulator: currentEmulator } }) => {
const loadState = async (): Promise<void> => {
if (await exists(savePath)) {
currentEmulator.loadState?.(await readFile(savePath));
}
setLoading(false);
emulatorRef.current = currentEmulator;
};
loadState();
};
window.EJS_onSaveState = ({ screenshot, state }) => {
window.EJS_terminate?.();
const saveState = async (): Promise<void> => {
if (!(await exists(SAVE_PATH))) await mkdirRecursive(SAVE_PATH);
if (await writeFile(savePath, Buffer.from(state), true)) {
const iconCacheRootPath = join(ICON_CACHE, SAVE_PATH);
const iconCachePath = join(
ICON_CACHE,
`${savePath}${ICON_CACHE_EXTENSION}`
);
if (!(await exists(iconCacheRootPath))) {
await mkdirRecursive(iconCacheRootPath);
updateFolder(dirname(SAVE_PATH));
}
await writeFile(iconCachePath, Buffer.from(screenshot), true);
updateFolder(SAVE_PATH, saveName);
}
};
if (state) saveState();
};
window.EJS_player = "#emulator";
window.EJS_biosUrl = "";
window.EJS_pathtodata = "Program Files/EmulatorJs/";
window.EJS_startOnLoaded = true;
window.EJS_RESET_VARS = true;
window.EJS_Buttons = {
cacheManage: false,
loadState: false,
quickLoad: false,
quickSave: false,
saveState: false,
screenRecord: false,
screenshot: false,
};
await loadFiles(["Program Files/EmulatorJs/loader.js"], undefined, true);
prependFileToTitle(`${window.EJS_gameName} (${console})`);
}, [
containerRef,
exists,
mkdirRecursive,
prependFileToTitle,
readFile,
setLoading,
updateFolder,
url,
writeFile,
]);
useEffect(() => {
if (url) loadRom();
else {
setLoading(false);
containerRef.current?.classList.add("drop");
}
}, [containerRef, loadRom, setLoading, url]);
useEffect(() => {
if (!loading) {
const canvas = containerRef.current?.querySelector("canvas");
if (canvas instanceof HTMLCanvasElement) {
linkElement(id, "peekElement", canvas);
}
}
return () => {
if (!loading && closing) {
emulatorRef.current?.elements.buttons.saveState?.click();
}
};
}, [closing, containerRef, id, linkElement, loading]);
};
export default useEmulator;