265 lines
8.7 KiB
JavaScript
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();
|
|
}
|
|
})();
|