Files
romm-web-ui/public/emu/loader.js

265 lines
8.7 KiB
JavaScript

const folderPath = (path) => {
const filename = path.split("/").pop();
return path.substring(0, path.length - filename.length);
};
function isAbsoluteUrl(path) {
return /^[a-zA-Z][\w.+-]*:\/\//i.test(path);
}
let scriptPath = (typeof window.EJS_pathtodata === "string") ? window.EJS_pathtodata : folderPath((new URL(document.currentScript.src)).pathname);
if (!scriptPath.endsWith("/")) {
scriptPath += "/";
}
if (!scriptPath.startsWith("/") && !isAbsoluteUrl(scriptPath)) {
scriptPath = "../" + scriptPath;
}
const debug = window.EJS_DEBUG_XX === true;
if (debug) {
console.log("Script Path:", scriptPath);
}
function resolvePath(path) {
if ("undefined" != typeof EJS_paths && typeof EJS_paths[path] === "string") {
return EJS_paths[path];
} else if (path.endsWith("emulator.min.js") || path.endsWith("css")) {
return scriptPath + path;
} else {
return scriptPath + "src/" + path;
}
}
async function loadScript(file) {
try {
const script = resolvePath(file);
const module = await import(script);
return module.default;
} catch(e) {
if (debug) console.error(e);
const module = await filesMissing(file);
return module.default;
}
}
function loadStyle(file) {
return new Promise(function(resolve) {
let css = document.createElement("link");
css.rel = "stylesheet";
css.href = resolvePath(file);
css.onload = resolve;
css.onerror = () => {
filesMissing(file).then(e => resolve());
}
document.head.appendChild(css);
})
}
async function filesMissing(file) {
console.error("Failed to load " + file);
let minifiedFailed = file.includes("min");
const errorMessage = `Failed to load ${file} because it's likely that the minified files are missing.
To fix this you have 3 options:
1. You can download the zip from the latest release here: https://github.com/EmulatorJS/EmulatorJS/releases/latest - Recommended
2. You can download the zip from here: https://cdn.emulatorjs.org/stable/data/emulator.min.zip and extract it to the data/ folder
3. You can build the files by running "npm i && npm run build" in the data/minify folder.`;
console[minifiedFailed ? "warn" : "error"](errorMessage);
if (minifiedFailed) {
console.log("Attempting to load non-minified files");
if (file === "emulator.min.js") {
return await loadScript("emulator.js");
} else {
await loadStyle("emulator.css");
}
}
}
function getLanguagePath(language) {
if ("undefined" != typeof EJS_paths && typeof EJS_paths[language] === "string") {
return { path: EJS_paths[language], fallback: null };
}
const base = scriptPath + "localization/" + language + ".json";
let fallback = null;
if (language.includes("-") || language.includes("_")) {
fallback = scriptPath + "localization/" + language.split(/[-_]/)[0] + ".json";
}
return { path: base, fallback };
}
async function fetchJson(path) {
try {
const response = await fetch(path);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (e) {
console.warn("Failed to fetch language file:", path, e.message);
return null;
}
}
function mergeLanguages(baseJson, overrideJson) {
if (!baseJson || !overrideJson) return baseJson || overrideJson || {};
return { ...baseJson, ...overrideJson };
}
async function loadLanguage(config) {
const defaultLangs = ["en", "en-US"];
if (!config.language || defaultLangs.includes(config.language)) return config;
console.log("Language:", config.language);
let langData = {};
const paths = getLanguagePath(config.language);
try {
const specificJson = await fetchJson(paths.path);
if (paths.fallback) {
const fallbackJson = await fetchJson(paths.fallback);
langData = mergeLanguages(fallbackJson, specificJson || {});
} else {
langData = specificJson || {};
}
config.langJson = langData;
} catch (e) {
if (paths.fallback) {
const fallbackLang = config.language.split(/[-_]/)[0];
console.warn(`Language '${config.language}' not found, trying '${fallbackLang}'`);
const fallbackJson = await fetchJson(paths.fallback);
if (fallbackJson) {
langData = fallbackJson;
config.langJson = langData;
config.language = fallbackLang;
}
} else {
console.warn(`No language file found for '${config.language}'`);
delete config.language;
delete config.langJson;
}
}
return config;
}
const config = {
debug: debug,
gameUrl: window.EJS_gameUrl,
dataPath: scriptPath,
system: window.EJS_core,
biosUrl: window.EJS_biosUrl,
gameName: window.EJS_gameName,
color: window.EJS_color,
adUrl: window.EJS_AdUrl,
adMode: window.EJS_AdMode,
adTimer: window.EJS_AdTimer,
adSize: window.EJS_AdSize,
alignStartButton: window.EJS_alignStartButton,
VirtualGamepadSettings: window.EJS_VirtualGamepadSettings,
buttonOpts: window.EJS_Buttons,
volume: window.EJS_volume,
defaultControllers: window.EJS_defaultControls,
startOnLoad: window.EJS_startOnLoaded,
fullscreenOnLoad: window.EJS_fullscreenOnLoaded,
filePaths: window.EJS_paths,
loadState: window.EJS_loadStateURL,
cacheLimit: window.EJS_CacheLimit,
cacheConfig: window.EJS_cacheConfig,
cheats: window.EJS_cheats,
cheatPath: window.EJS_cheatPath,
defaultOptions: window.EJS_defaultOptions,
gamePatchUrl: window.EJS_gamePatchUrl,
gameParentUrl: window.EJS_gameParentUrl,
netplayUrl: window.EJS_netplayServer,
netplayICEServers: window.EJS_netplayICEServers,
gameId: window.EJS_gameID,
backgroundImg: window.EJS_backgroundImage,
backgroundBlur: window.EJS_backgroundBlur,
backgroundColor: window.EJS_backgroundColor,
controlScheme: window.EJS_controlScheme,
threads: window.EJS_threads,
disableCue: window.EJS_disableCue,
startBtnName: window.EJS_startButtonName,
softLoad: window.EJS_softLoad,
capture: window.EJS_screenCapture,
externalFiles: window.EJS_externalFiles,
dontExtractRom: window.EJS_dontExtractRom,
dontExtractBIOS: window.EJS_dontExtractBIOS,
disableLocalStorage: window.EJS_disableLocalStorage,
forceLegacyCores: window.EJS_forceLegacyCores,
noAutoFocus: window.EJS_noAutoFocus,
videoRotation: window.EJS_videoRotation,
hideSettings: window.EJS_hideSettings,
browserMode: window.EJS_browserMode,
additionalShaders: window.EJS_shaders,
fixedSaveInterval: window.EJS_fixedSaveInterval,
disableAutoUnload: window.EJS_disableAutoUnload,
disableBatchBootup: window.EJS_disableBatchBootup
};
async function prepareLanguage() {
try {
const systemLang = Intl.DateTimeFormat().resolvedOptions().locale;
config.language = window.EJS_language || systemLang;
if (config.language && window.EJS_disableAutoLang !== false) {
return await loadLanguage(config);
}
} catch (e) {
console.warn("Language detection failed:", e.message);
delete config.language;
delete config.langJson;
}
}
(async function() {
let EmulatorJS;
if (debug) {
EmulatorJS = await loadScript("emulator.js");
await loadStyle("emulator.css");
} else {
EmulatorJS = await loadScript("emulator.min.js");
await loadStyle("emulator.min.css");
}
if (!EmulatorJS) {
console.error("EmulatorJS failed to load. Check for missing files.");
return;
}
await prepareLanguage();
if (debug) {
console.log("Language:", config.language);
console.log("Language JSON loaded:", !!config.langJson);
}
window.EJS_emulator = new EmulatorJS(EJS_player, config);
window.EJS_adBlocked = (url, del) => window.EJS_emulator.adBlocked(url, del);
const handlers = [
["ready", window.EJS_ready],
["start", window.EJS_onGameStart],
["loadState", window.EJS_onLoadState],
["saveState", window.EJS_onSaveState],
["loadSave", window.EJS_onLoadSave],
["saveSave", window.EJS_onSaveSave]
];
handlers.forEach(([event, callback]) => {
if (typeof callback === "function") {
window.EJS_emulator.on(event, callback);
}
});
if (typeof window.EJS_onSaveUpdate === "function") {
window.EJS_emulator.on("saveUpdate", window.EJS_onSaveUpdate);
window.EJS_emulator.enableSaveUpdateEvent();
}
})();