From ec42fcee2567a18d40a8b27dc305fa5e2017249a Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Sat, 5 Apr 2025 20:47:39 -0500 Subject: [PATCH 001/106] Add hook for save database loaded --- data/src/GameManager.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/src/GameManager.js b/data/src/GameManager.js index 57d0556ec..49dde9c32 100644 --- a/data/src/GameManager.js +++ b/data/src/GameManager.js @@ -39,7 +39,6 @@ class EJS_GameManager { this.initShaders(); this.EJS.on("exit", () => { - clearInterval(this.saveInterval); if (!this.EJS.failedToStart) { this.saveSaveFiles(); this.functions.restart(); @@ -55,7 +54,6 @@ class EJS_GameManager { }; }, 1000); }) - this.saveInterval = setInterval(this.saveSaveFiles.bind(this), 1000); } mountFileSystems() { return new Promise(async resolve => { @@ -63,6 +61,7 @@ class EJS_GameManager { this.mkdir("/data/saves"); this.FS.mount(this.FS.filesystems.IDBFS, {autoPersist: true}, '/data/saves'); this.FS.syncfs(true, resolve); + this.EJS.callEvent("saveDatabaseLoaded", this.FS); }); } writeConfigFile() { From 03cab1d1151c6c9d87359be52f83219064a226ce Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Sat, 5 Apr 2025 20:58:42 -0500 Subject: [PATCH 002/106] Add save interval option --- data/src/emulator.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/data/src/emulator.js b/data/src/emulator.js index f7ee6f9b7..9a50d65f8 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -3924,6 +3924,16 @@ class EmulatorJS { this.gameManager.setVideoRotation(0); this.videoRotationChanged = true; } + } else if (option === "save-save-interval") { + value = parseInt(value); + if (this.saveSaveInterval !== null) { + clearInterval(this.saveSaveInterval); + } + // Disabled + if (value === 0 || isNaN(value)) return; + this.gameManager.saveSaveFiles(); + if (this.debug) console.log("Saving every", value * 1000, "miliseconds"); + setInterval(() => this.gameManager.saveSaveFiles(), value * 1000); } } menuOptionChanged(option, value) { @@ -4431,6 +4441,15 @@ class EmulatorJS { 'download': this.localization("Download"), 'browser': this.localization("Keep in Browser") }, 'download', saveStateOpts, true); + addToMenu(this.localization("System Save interval"), 'save-save-interval', { + "0": "Disabled", + "30": "30 seconds", + "60": "1 minute", + "300": "5 minutes", + "600": "10 minutes", + "900": "15 minutes", + "1800": "30 minutes" + }, '300', saveStateOpts, true); } if (this.touch || navigator.maxTouchPoints > 0) { From 0ee5cc9f064009205c908d1073cb50aa5f68edb0 Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Sat, 5 Apr 2025 21:48:12 -0500 Subject: [PATCH 003/106] Add ability to hide some, but not all settings --- data/loader.js | 1 + data/src/emulator.js | 49 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/data/loader.js b/data/loader.js index 5317b5751..32bfc9e37 100644 --- a/data/loader.js +++ b/data/loader.js @@ -121,6 +121,7 @@ config.forceLegacyCores = window.EJS_forceLegacyCores; config.noAutoFocus = window.EJS_noAutoFocus; config.videoRotation = window.EJS_videoRotation; + config.hideSettings = window.EJS_hideSettings; config.shaders = Object.assign({}, window.EJS_SHADERS, window.EJS_shaders ? window.EJS_shaders : {}); if (typeof window.EJS_language === "string" && window.EJS_language !== "en-US") { diff --git a/data/src/emulator.js b/data/src/emulator.js index 9a50d65f8..644dd3ebd 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -3989,6 +3989,7 @@ class EmulatorJS { } let allOpts = {}; + // TODO - Why is this duplicated? const addToMenu = (title, id, options, defaultOption) => { const span = this.createElement("span"); span.innerText = title; @@ -4136,6 +4137,7 @@ class EmulatorJS { nested.classList.add("ejs_settings_transition"); this.settings = {}; const menus = []; + let parentMenuCt = 0; const createSettingParent = (child, title, parentElement) => { const rv = this.createElement("div"); @@ -4153,6 +4155,7 @@ class EmulatorJS { const menu = this.createElement("div"); menus.push(menu); + parentMenuCt++; menu.style.overflow = "auto"; menu.setAttribute("hidden", ""); const button = this.createElement("button"); @@ -4170,6 +4173,24 @@ class EmulatorJS { menu.removeAttribute("hidden"); parentElement.setAttribute("hidden", ""); }) + const observer = new MutationObserver((list) => { + for (const k of list) { + for (const removed of k.removedNodes) { + if (removed === menu) { + menuOption.remove(); + observer.disconnect(); + const index = menus.indexOf(menu); + if (index !== -1) menus.splice(index, 1); + this.settingsMenu.style.display = ""; + const homeSize = this.getElementSize(parentElement); + nested.style.width = (homeSize.width+20) + "px"; + nested.style.height = homeSize.height + "px"; + // This SHOULD always be called before the game started - this SHOULD never be an issue + this.settingsMenu.style.display = "none"; + } + } + } + }); this.addEventListener(button, "click", goToHome); button.type = "button"; @@ -4187,10 +4208,21 @@ class EmulatorJS { menu.appendChild(rv); nested.appendChild(menu); + observer.observe(nested, { + childList: true, + subtree: true, + }); } return rv; } + + const checkForEmptyMenu = (element) => { + if (element.firstChild === null) { + element.parentElement.remove(); // No point in keeping an empty menu + parentMenuCt--; + } + } const home = createSettingParent(); @@ -4233,6 +4265,9 @@ class EmulatorJS { let allOpts = {}; const addToMenu = (title, id, options, defaultOption, parentElement, useParentParent) => { + if (Array.isArray(this.config.hideSettings) && this.config.hideSettings.includes(id)) { + return; + } parentElement = parentElement || home; const transitionElement = useParentParent ? parentElement.parentElement : parentElement; const menuOption = this.createElement("div"); @@ -4450,6 +4485,7 @@ class EmulatorJS { "900": "15 minutes", "1800": "30 minutes" }, '300', saveStateOpts, true); + checkForEmptyMenu(saveStateOpts); } if (this.touch || navigator.maxTouchPoints > 0) { @@ -4462,6 +4498,7 @@ class EmulatorJS { 'enabled': this.localization("Enabled"), 'disabled': this.localization("Disabled") }, 'disabled', virtualGamepad, true); + checkForEmptyMenu(virtualGamepad); } let coreOpts; @@ -4487,6 +4524,7 @@ class EmulatorJS { coreOptions, true); }) + checkForEmptyMenu(coreOptions); } @@ -4513,13 +4551,18 @@ class EmulatorJS { retroarchOptsMenu, true); }) + checkForEmptyMenu(retroarchOptsMenu); } + checkForEmptyMenu(graphicsOptions); + checkForEmptyMenu(speedOptions); + this.settingsMenu.appendChild(nested); this.settingParent.appendChild(this.settingsMenu); this.settingParent.style.position = "relative"; + this.settingsMenu.style.display = ""; const homeSize = this.getElementSize(home); nested.style.width = (homeSize.width+20) + "px"; nested.style.height = homeSize.height + "px"; @@ -4535,6 +4578,12 @@ class EmulatorJS { this.changeSettingOption(k, this.config.defaultOptions[k]); } } + + if (parentMenuCt === 0) { + this.on("start", () => { + this.elements.bottomBar.settings[0][0].style.display = "none"; + }); + } } createSubPopup(hidden) { const popup = this.createElement('div'); From 8df1acf4466b53dfd7951eec6d6f08bef83d7093 Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Sat, 5 Apr 2025 21:50:46 -0500 Subject: [PATCH 004/106] Fix up save state interval logic --- data/src/emulator.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/data/src/emulator.js b/data/src/emulator.js index 644dd3ebd..0b08ad6ab 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -3926,14 +3926,17 @@ class EmulatorJS { } } else if (option === "save-save-interval") { value = parseInt(value); - if (this.saveSaveInterval !== null) { + if (this.saveSaveInterval && this.saveSaveInterval !== null) { clearInterval(this.saveSaveInterval); + this.saveSaveInterval = null; } // Disabled if (value === 0 || isNaN(value)) return; - this.gameManager.saveSaveFiles(); + if (this.started) this.gameManager.saveSaveFiles(); if (this.debug) console.log("Saving every", value * 1000, "miliseconds"); - setInterval(() => this.gameManager.saveSaveFiles(), value * 1000); + this.saveSaveInterval = setInterval(() => { + if (this.started) this.gameManager.saveSaveFiles(); + }, value * 1000); } } menuOptionChanged(option, value) { From 9aab1c54a99763f88b4b7ea6c515fa1830d9fe93 Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Sat, 5 Apr 2025 22:00:35 -0500 Subject: [PATCH 005/106] Add ability to pull system language --- data/loader.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/data/loader.js b/data/loader.js index 32bfc9e37..923b5c8e4 100644 --- a/data/loader.js +++ b/data/loader.js @@ -124,18 +124,26 @@ config.hideSettings = window.EJS_hideSettings; config.shaders = Object.assign({}, window.EJS_SHADERS, window.EJS_shaders ? window.EJS_shaders : {}); - if (typeof window.EJS_language === "string" && window.EJS_language !== "en-US") { + let systemLang; + try { + systemLang = Intl.DateTimeFormat().resolvedOptions().locale; + } catch(e) {} //Ignore + if ((typeof window.EJS_language === "string" && window.EJS_language !== "en-US") || (systemLang && window.EJS_disableAutoLang !== false)) { + const language = window.EJS_language || systemLang; try { let path; - if ('undefined' != typeof EJS_paths && typeof EJS_paths[window.EJS_language] === 'string') { - path = EJS_paths[window.EJS_language]; + console.log("Loading language", language); + if ('undefined' != typeof EJS_paths && typeof EJS_paths[language] === 'string') { + path = EJS_paths[language]; } else { - path = scriptPath+"localization/"+window.EJS_language+".json"; + path = scriptPath+"localization/"+language+".json"; } - config.language = window.EJS_language; + config.language = language; config.langJson = JSON.parse(await (await fetch(path)).text()); } catch(e) { - config.langJson = {}; + console.log("Missing language", language, "!!"); + delete config.language; + delete config.langJson; } } From 8b5d605a1708dc1245166f8774abae2c9ecdd14f Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Sat, 5 Apr 2025 22:09:55 -0500 Subject: [PATCH 006/106] Correct placement of saveDatabaseLoaded invoke call --- data/src/GameManager.js | 1 - data/src/emulator.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/data/src/GameManager.js b/data/src/GameManager.js index 49dde9c32..3a8ec21b1 100644 --- a/data/src/GameManager.js +++ b/data/src/GameManager.js @@ -61,7 +61,6 @@ class EJS_GameManager { this.mkdir("/data/saves"); this.FS.mount(this.FS.filesystems.IDBFS, {autoPersist: true}, '/data/saves'); this.FS.syncfs(true, resolve); - this.EJS.callEvent("saveDatabaseLoaded", this.FS); }); } writeConfigFile() { diff --git a/data/src/emulator.js b/data/src/emulator.js index 0b08ad6ab..bc82c172f 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -893,6 +893,7 @@ class EmulatorJS { this.gameManager = new window.EJS_GameManager(this.Module, this); await this.gameManager.loadExternalFiles(); await this.gameManager.mountFileSystems(); + this.callEvent("saveDatabaseLoaded", this.gameManager.FS); if (this.getCore() === "ppsspp") { await this.gameManager.loadPpssppAssets(); } From 0b6c5f1e27f1848abd63682cdb44fa9c32ba329b Mon Sep 17 00:00:00 2001 From: noel-forester <62397558+noel-forester@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:50:20 +0900 Subject: [PATCH 007/106] Update ja-JA.json Edit the Japanese translation file --- data/localization/ja-JA.json | 266 +++++++++++++++++------------------ 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/data/localization/ja-JA.json b/data/localization/ja-JA.json index 1c0b012a7..272445a2c 100644 --- a/data/localization/ja-JA.json +++ b/data/localization/ja-JA.json @@ -11,111 +11,111 @@ "9": "9", "Restart": "再起動", "Pause": "一時停止", - "Play": "遊ぶ", - "Save State": "状態を保存", - "Load State": "ロード状態", - "Control Settings": "コントロール設定", + "Play": "開始", + "Save State": "ステートセーブ", + "Load State": "ステートロード", + "Control Settings": "キーコンフィグ", "Cheats": "チート", "Cache Manager": "キャッシュマネージャー", - "Export Save File": "保存ファイルのエクスポート", - "Import Save File": "保存ファイルのインポート", + "Export Save File": "セーブデータをエクスポート", + "Import Save File": "セーブデータをインポート", "Netplay": "ネットプレイ", "Mute": "ミュート", - "Unmute": "ミュートを解除する", + "Unmute": "ミュート解除", "Settings": "設定", - "Enter Fullscreen": "全画面表示に入る", - "Exit Fullscreen": "全画面表示を終了する", + "Enter Fullscreen": "全画面表示", + "Exit Fullscreen": "全画面表示を終了", "Reset": "リセット", "Clear": "クリア", - "Close": "近い", - "QUICK SAVE STATE": "状態のクイック保存", - "QUICK LOAD STATE": "クイックロード状態", - "CHANGE STATE SLOT": "状態スロットの変更", + "Close": "閉じる", + "QUICK SAVE STATE": "クイックセーブ", + "QUICK LOAD STATE": "クイックロード", + "CHANGE STATE SLOT": "セーブスロットの変更", "FAST FORWARD": "早送り", - "Player": "プレーヤー", + "Player": "プレイヤー", "Connected Gamepad": "接続されたゲームパッド", "Gamepad": "ゲームパッド", "Keyboard": "キーボード", "Set": "セット", "Add Cheat": "チートの追加", - "Create a Room": "ルームを作成する", + "Create a Room": "部屋を作成", "Rooms": "部屋", - "Start Game": "ゲームをスタート", + "Start Game": "ゲームスタート", "Loading...": "読み込み中...", "Download Game Core": "ゲームコアをダウンロード", - "Decompress Game Core": "ゲームコアを解凍する", + "Decompress Game Core": "ゲームコアを解凍", "Download Game Data": "ゲームデータのダウンロード", - "Decompress Game Data": "ゲームデータを解凍する", - "Shaders": "シェーダ", + "Decompress Game Data": "ゲームデータを解凍", + "Shaders": "シェーダー", "Disabled": "無効", - "2xScaleHQ": "2xスケール本社", - "4xScaleHQ": "4xスケール本社", - "CRT easymode": "CRTイージーモード", - "CRT aperture": "CRTの絞り", - "CRT geom": "CRT ジオム", - "CRT mattias": "CRT マティアス", + "2xScaleHQ": "2xスケール", + "4xScaleHQ": "4xスケール", + "CRT easymode": "CRT (イージーモード)", + "CRT aperture": "CRT (アパーチャ)", + "CRT geom": "CRT (ジオメ)", + "CRT mattias": "CRT (Mattias)", "FPS": "FPS", - "show": "見せる", - "hide": "隠れる", - "Fast Forward Ratio": "早送り率", + "show": "表示", + "hide": "非表示", + "Fast Forward Ratio": "早送り速度", "Fast Forward": "早送り", "Enabled": "有効", - "Save State Slot": "状態保存スロット", - "Save State Location": "状態の保存場所", + "Save State Slot": "ステートセーブスロット", + "Save State Location": "ステートセーブ保存場所", "Download": "ダウンロード", "Keep in Browser": "ブラウザ内に保持", "Auto": "自動", "NTSC": "NTSC", - "PAL": "パル", - "Dendy": "デンディ", - "8:7 PAR": "8:7 パー", + "PAL": "PAL", + "Dendy": "Dendy", + "8:7 PAR": "8:7 PAR", "4:3": "4:3", - "Low": "低い", - "High": "高い", - "Very High": "すごく高い", + "Low": "低", + "High": "高", + "Very High": "超高", "None": "なし", - "Player 1": "プレイヤー 1", + "Player 1": "プレイヤー1", "Player 2": "プレイヤー2", "Both": "両方", - "SAVED STATE TO SLOT": "状態をスロットに保存しました", - "LOADED STATE FROM SLOT": "スロットからロードされた状態", + "SAVED STATE TO SLOT": "状態をスロットにセーブしました", + "LOADED STATE FROM SLOT": "状態をスロットからロードしました", "SET SAVE STATE SLOT TO": "状態保存スロットを次のように設定します", "Network Error": "ネットワークエラー", - "Submit": "提出する", + "Submit": "適用", "Description": "説明", "Code": "コード", "Add Cheat Code": "チートコードを追加する", - "Leave Room": "部屋を出る", + "Leave Room": "部屋を退出", "Password": "パスワード", "Password (optional)": "パスワード (オプション)", "Max Players": "最大プレイヤー数", "Room Name": "部屋の名前", "Join": "参加する", - "Player Name": "プレーヤの名前", + "Player Name": "プレイヤー名", "Set Player Name": "プレイヤー名の設定", "Left Handed Mode": "左利きモード", "Virtual Gamepad": "仮想ゲームパッド", "Disk": "ディスク", "Press Keyboard": "キーボードを押す", "INSERT COIN": "コインを入れる", - "Remove": "取り除く", - "SAVE LOADED FROM BROWSER": "ブラウザからロードして保存", - "SAVE SAVED TO BROWSER": "保存 ブラウザに保存", - "Join the discord": "ディスコードに参加する", + "Remove": "削除", + "SAVE LOADED FROM BROWSER": "セーブをブラウザからロード", + "SAVE SAVED TO BROWSER": "セーブをブラウザに保存", + "Join the discord": "ディスコードに参加", "View on GitHub": "GitHub で見る", "Failed to start game": "ゲームの開始に失敗しました", - "Download Game BIOS": "ゲーム BIOS をダウンロードする", - "Decompress Game BIOS": "ゲーム BIOS を解凍する", + "Download Game BIOS": "ゲームBIOSをダウンロード", + "Decompress Game BIOS": "ゲームBIOSを解凍", "Download Game Parent": "ゲームの親をダウンロード", - "Decompress Game Parent": "ゲームの親を解凍する", + "Decompress Game Parent": "ゲームの親を解凍", "Download Game Patch": "ゲームパッチをダウンロード", - "Decompress Game Patch": "ゲームパッチを解凍する", + "Decompress Game Patch": "ゲームパッチを解凍", "Download Game State": "ゲームステートのダウンロード", "Check console": "コンソールを確認してください", "Error for site owner": "サイト所有者のエラー", "EmulatorJS": "エミュレータJS", "Clear All": "すべてクリア", - "Take Screenshot": "スクリーンショットを撮ります", + "Take Screenshot": "スクリーンショットを撮影", "Quick Save": "クイックセーブ", "Quick Load": "クイックロード", "REWIND": "巻き戻し", @@ -123,74 +123,74 @@ "Rewind Granularity": "巻き戻し粒度", "Slow Motion Ratio": "スローモーション比率", "Slow Motion": "スローモーション", - "Home": "家", + "Home": "ホーム", "EmulatorJS License": "エミュレータJSライセンス", "RetroArch License": "RetroArch ライセンス", "SLOW MOTION": "スローモーション", - "A": "あ", + "A": "A", "B": "B", - "SELECT": "選択する", - "START": "始める", + "SELECT": "SELECT", + "START": "START", "UP": "上", "DOWN": "下", "LEFT": "左", "RIGHT": "右", - "X": "バツ", + "X": "X", "Y": "Y", "L": "L", "R": "R", "Z": "Z", - "STICK UP": "上に突き出る", - "STICK DOWN": "スティックダウン", - "STICK LEFT": "左スティック", - "STICK RIGHT": "右スティック", - "C-PAD UP": "Cパッドアップ", - "C-PAD DOWN": "C-パッドを下へ", - "C-PAD LEFT": "C-パッド左", - "C-PAD RIGHT": "Cパッド右", - "MICROPHONE": "マイクロフォン", - "BUTTON 1 / START": "ボタン 1 / スタート", + "STICK UP": "スティック上", + "STICK DOWN": "スティック下", + "STICK LEFT": "スティック左", + "STICK RIGHT": "スティック右", + "C-PAD UP": "Cボタン上", + "C-PAD DOWN": "Cボタン下", + "C-PAD LEFT": "Cボタン左", + "C-PAD RIGHT": "Cボタン右", + "MICROPHONE": "マイク", + "BUTTON 1 / START": "ボタン1 / スタート", "BUTTON 2": "ボタン2", "BUTTON": "ボタン", - "LEFT D-PAD UP": "左方向パッドを上に", - "LEFT D-PAD DOWN": "左方向パッド下", - "LEFT D-PAD LEFT": "左十字キー左", - "LEFT D-PAD RIGHT": "左十字キー右", - "RIGHT D-PAD UP": "右方向パッド上", - "RIGHT D-PAD DOWN": "右方向パッド下", + "LEFT D-PAD UP": "十字キー上", + "LEFT D-PAD DOWN": "十字キー下", + "LEFT D-PAD LEFT": "十字キー左", + "LEFT D-PAD RIGHT": "十字キー右", + "RIGHT D-PAD UP": "右十字キー上", + "RIGHT D-PAD DOWN": "右十字キー下", "RIGHT D-PAD LEFT": "右十字キー左", "RIGHT D-PAD RIGHT": "右十字キー右", "C": "C", "MODE": "モード", - "FIRE": "火", + "FIRE": "発射", "RESET": "リセット", - "LEFT DIFFICULTY A": "左の難易度A", - "LEFT DIFFICULTY B": "左の難易度B", - "RIGHT DIFFICULTY A": "右の難易度A", - "RIGHT DIFFICULTY B": "右の難易度B", - "COLOR": "色", + "LEFT DIFFICULTY A": "左難易度A", + "LEFT DIFFICULTY B": "左難易度B", + "RIGHT DIFFICULTY A": "右難易度A", + "RIGHT DIFFICULTY B": "右難易度B", + "COLOR": "カラー", "B/W": "白黒", "PAUSE": "一時停止", "OPTION": "オプション", "OPTION 1": "オプション1", - "OPTION 2": "オプション 2", + "OPTION 2": "オプション2", "L2": "L2", "R2": "R2", "L3": "L3", "R3": "R3", - "L STICK UP": "Lスティックアップ", - "L STICK DOWN": "Lスティックダウン", - "L STICK LEFT": "Lスティック左", - "L STICK RIGHT": "Lスティック右", - "R STICK UP": "Rスティックアップ", - "R STICK DOWN": "Rスティックダウン", - "R STICK LEFT": "Rスティック左", - "R STICK RIGHT": "R右スティック", - "Start": "始める", - "Select": "選択する", - "Fast": "速い", - "Slow": "遅い", - "a": "ある", + "L STICK UP": "左スティック上", + "L STICK DOWN": "左スティック下", + "L STICK LEFT": "左スティック左", + "L STICK RIGHT": "左スティック右", + "R STICK UP": "右スティック上", + "R STICK DOWN": "右スティック下", + "R STICK LEFT": "右スティック左", + "R STICK RIGHT": "右スティック右", + "Start": "Start", + "Select": "Select", + "Fast": "Fast", + "Slow": "Slow", + "a": "a", "b": "b", "c": "c", "d": "d", @@ -198,34 +198,34 @@ "f": "f", "g": "g", "h": "h", - "i": "私", + "i": "i", "j": "j", "k": "k", - "l": "私", - "m": "メートル", + "l": "l", + "m": "m", "n": "n", - "o": "ああ", + "o": "o", "p": "p", "q": "q", "r": "r", "s": "s", "t": "t", - "u": "あなた", + "u": "u", "v": "v", "w": "w", - "x": "バツ", + "x": "x", "y": "y", "z": "z", - "enter": "入力", - "escape": "逃げる", - "space": "空間", - "tab": "タブ", - "backspace": "バックスペース", - "delete": "消去", - "arrowup": "アローアップ", - "arrowdown": "矢印", + "enter": "enter", + "escape": "escape", + "space": "space", + "tab": "tab", + "backspace": "backspace", + "delete": "delete", + "arrowup": "矢印上", + "arrowdown": "矢印下", "arrowleft": "矢印左", - "arrowright": "右矢印", + "arrowright": "矢印右", "f1": "f1", "f2": "f2", "f3": "f3", @@ -238,16 +238,16 @@ "f10": "f10", "f11": "f11", "f12": "f12", - "shift": "シフト", - "control": "コントロール", - "alt": "代替", - "meta": "メタ", - "capslock": "キャップスロック", - "insert": "入れる", - "home": "家", - "end": "終わり", - "pageup": "ページアップ", - "pagedown": "ページダウン", + "shift": "shift", + "control": "control", + "alt": "alt", + "meta": "meta", + "capslock": "capslock", + "insert": "insert", + "home": "home", + "end": "end", + "pageup": "pageup", + "pagedown": "pagedown", "!": "!", "@": "@", "#": "#", @@ -263,39 +263,39 @@ "+": "+", "=": "=", "[": "[", - "]": "】", + "]": "]", "{": "{", "}": "}", ";": ";", ":": ":", "'": "'", - "\"": "」", - ",": "、", - ".": "。", + "\"": "\"", + ",": ",", + ".": ".", "<": "<", ">": ">", "/": "/", "?": "?", - "LEFT_STICK_X": "LEFT_STICK_X", + "LEFT_STICK_X": "左スティック_X", "LEFT_STICK_Y": "左スティック_Y", - "RIGHT_STICK_X": "RIGHT_STICK_X", + "RIGHT_STICK_X": "右スティック_X", "RIGHT_STICK_Y": "右スティック_Y", - "LEFT_TRIGGER": "LEFT_TRIGGER", + "LEFT_TRIGGER": "左トリガー", "RIGHT_TRIGGER": "右トリガー", - "A_BUTTON": "ボタン", + "A_BUTTON": "A_ボタン", "B_BUTTON": "B_ボタン", "X_BUTTON": "X_ボタン", "Y_BUTTON": "Y_ボタン", "START_BUTTON": "スタートボタン", - "SELECT_BUTTON": "SELECT_BUTTON", + "SELECT_BUTTON": "セレクトボタン", "L1_BUTTON": "L1_ボタン", "R1_BUTTON": "R1_ボタン", "L2_BUTTON": "L2_ボタン", "R2_BUTTON": "R2_ボタン", - "LEFT_THUMB_BUTTON": "LEFT_THUMB_BUTTON", - "RIGHT_THUMB_BUTTON": "RIGHT_THUMB_BUTTON", - "DPAD_UP": "DPAD_UP", - "DPAD_DOWN": "DPAD_DOWN", - "DPAD_LEFT": "DPAD_LEFT", - "DPAD_RIGHT": "DPAD_RIGHT" -} \ No newline at end of file + "LEFT_THUMB_BUTTON": "左スティック押し込み", + "RIGHT_THUMB_BUTTON": "右スティック押し込み", + "DPAD_UP": "十字キー上", + "DPAD_DOWN": "十字キー下", + "DPAD_LEFT": "十字キー左", + "DPAD_RIGHT": "十字キー右" +} From 03be36b16528e2f288489a7dea4e0c2cfc10f8fb Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Mon, 14 Apr 2025 10:40:20 -0500 Subject: [PATCH 008/106] Dropdown for gamepad selection --- data/emulator.css | 15 +++++++++++++ data/src/emulator.js | 51 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/data/emulator.css b/data/emulator.css index 6ab06b2cb..33f087d4d 100644 --- a/data/emulator.css +++ b/data/emulator.css @@ -1396,3 +1396,18 @@ .ejs_netplay_table_row:hover { background-color: #2d2d2d; } + +.ejs_gamepad_dropdown { + background-color: var(--ejs-background-color); + color: white; + border: none; + padding: 8px 12px; + border-radius: 6px; + font-family: inherit; + outline: none; + cursor: pointer; +} + +.ejs_gamepad_dropdown:focus { + box-shadow: 0 0 0 2px rgba(51, 153, 255, 0.6); +} diff --git a/data/src/emulator.js b/data/src/emulator.js index bc82c172f..ea85114a9 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -1112,6 +1112,12 @@ class EmulatorJS { this.gamepad = new GamepadHandler(); //https://github.com/ethanaobrien/Gamepad this.gamepad.on('connected', (e) => { if (!this.gamepadLabels) return; + for (let i=0; i { @@ -1135,11 +1141,18 @@ class EmulatorJS { } updateGamepadLabels() { for (let i=0; i buttonListeners.forEach(elem => elem()); this.gamepadLabels = []; + this.gamepadSelection = []; this.controls = JSON.parse(JSON.stringify(this.defaultControllers)); const body = this.createPopup("Control Settings", { "Reset": () => { @@ -2523,9 +2537,32 @@ class EmulatorJS { gamepadTitle.style = "font-size:12px;"; gamepadTitle.innerText = this.localization("Connected Gamepad")+": "; - const gamepadName = this.createElement("span"); + const gamepadName = this.createElement("select"); + gamepadName.classList.add("ejs_gamepad_dropdown"); + gamepadName.setAttribute("title", "gamepad-"+i); + gamepadName.setAttribute("index", i); this.gamepadLabels.push(gamepadName); - gamepadName.innerText = "n/a"; + this.gamepadSelection.push(""); + this.addEventListener(gamepadName, "change", e => { + const controller = e.target.value; + const player = parseInt(e.target.getAttribute("index")); + if (controller === "notconnected") { + this.gamepadSelection[player] = ""; + } else { + for (let i=0; i Date: Mon, 14 Apr 2025 10:47:25 -0500 Subject: [PATCH 009/106] Push by @jurcaalexandrucristian --- data/localization/readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/localization/readme.md b/data/localization/readme.md index a3fa627aa..96c705bb8 100644 --- a/data/localization/readme.md +++ b/data/localization/readme.md @@ -19,6 +19,7 @@ Supported languages `it-IT` - Italian
`tr-Tr` - Turkish
`fa-AF` - Persian
+`ro-RO` - Romanian
default: `en-US` @@ -41,7 +42,9 @@ Translated for `it-IT` by [@IvanMazzoli](https://github.com/IvanMazzoli)
Translated for `tr-Tr` by [@iGoodie](https://github.com/iGoodie)
Translated for `fa-AF` by [@rezamohdev](https://github.com/rezamohdev)
Translated for `af-FR` by [@t3chnob0y](https://github.com/t3chnob0y)
-Translated for `ja-JA`, `hi-HI`, `ar-AR`, `jv-JV`, `ben-BEN`, `ru-RU`, `de-GER`, `ko-KO` by [@allancoding](https://github.com/allancoding), using a translate application
+Translated for `ro-RO` by [@jurcaalexandrucristian](https://github.com/jurcaalexandrucristian)
+Translated for `ja-JA` by [@noel-forester](https://github.com/noel-forester)
+Translated for `hi-HI`, `ar-AR`, `jv-JV`, `ben-BEN`, `ru-RU`, `de-GER`, `ko-KO` by [@allancoding](https://github.com/allancoding), using a translate application
## Contributing From d48118b90b6357f5c6af14e121295232e03c1ea0 Mon Sep 17 00:00:00 2001 From: Ethan O'Brien Date: Mon, 14 Apr 2025 12:14:06 -0500 Subject: [PATCH 010/106] Fix gamepadIndex in gamepadEvent function --- data/src/emulator.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/data/src/emulator.js b/data/src/emulator.js index ea85114a9..b415bce2e 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -3047,6 +3047,10 @@ class EmulatorJS { } gamepadEvent(e) { if (!this.started) return; + const gamepadIndex = this.gamepadSelection.indexOf(this.gamepad.gamepads[e.gamepadIndex].id); + if (gamepadIndex < 0) { + return; // Gamepad not set anywhere + } const value = function(value) { if (value > 0.5 || value < -0.5) { return (value > 0) ? 1 : -1; @@ -3058,7 +3062,7 @@ class EmulatorJS { if ('buttonup' === e.type || (e.type === "axischanged" && value === 0)) return; const num = this.controlPopup.getAttribute("button-num"); const player = parseInt(this.controlPopup.getAttribute("player-num")); - if (e.gamepadIndex !== player) return; + if (gamepadIndex !== player) return; if (!this.controls[player][num]) { this.controls[player][num] = {}; } @@ -3071,7 +3075,7 @@ class EmulatorJS { if (this.settingsMenu.style.display !== "none" || this.isPopupOpen()) return; const special = [16, 17, 18, 19, 20, 21, 22, 23]; for (let i=0; i<4; i++) { - if (e.gamepadIndex !== i) continue; + if (gamepadIndex !== i) continue; for (let j=0; j<30; j++) { if (!this.controls[i][j] || this.controls[i][j].value2 === undefined) { continue; From e66448117562fcd9f6fc89c03799009673fcc379 Mon Sep 17 00:00:00 2001 From: Allan Niles Date: Wed, 16 Apr 2025 10:48:59 -0600 Subject: [PATCH 011/106] refactor minify script --- .github/workflows/latest.yml | 6 ++--- .github/workflows/stable.yml | 7 +----- .gitignore | 3 +-- README.md | 6 +++++ data/minify/index.js | 28 ----------------------- data/minify/package.json | 22 ------------------ {data/minify => minify}/README.md | 20 +++++++--------- minify/minify.js | 38 +++++++++++++++++++++++++++++++ package.json | 37 ++++++++++++++++++------------ 9 files changed, 80 insertions(+), 87 deletions(-) delete mode 100644 data/minify/index.js delete mode 100644 data/minify/package.json rename {data/minify => minify}/README.md (50%) create mode 100644 minify/minify.js diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index a1506193c..8405deb02 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -24,15 +24,15 @@ jobs: git pull - name: Minify Files run: | - cd /mnt/HDD/public/.EmulatorJS/data/minify/ + cd /mnt/HDD/public/.EmulatorJS/ npm i - npm run build + npm run minify - name: Zip Minified Files run: | cd /mnt/HDD/public/.EmulatorJS/data/ zip emulator.min.zip emulator.min.js emulator.min.css - name: Clean Up Minify run: | - cd /mnt/HDD/public/.EmulatorJS/data/ + cd /mnt/HDD/public/.EmulatorJS/ rm "minify/package-lock.json" rm -rf "minify/node_modules/" \ No newline at end of file diff --git a/.github/workflows/stable.yml b/.github/workflows/stable.yml index c5003e070..65ef1989f 100644 --- a/.github/workflows/stable.yml +++ b/.github/workflows/stable.yml @@ -66,9 +66,4 @@ jobs: run: | cd /mnt/HDD/public/ rm -f "$OLD_VERSION/data/emulator.min.zip" - rm -rf "$OLD_VERSION/data/minify/node_modules/" - - - - - + rm -rf "$OLD_VERSION/node_modules/" diff --git a/.gitignore b/.gitignore index 6116e25cd..e7c875191 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ **/node_modules/ *.db -data/minify/package-lock.json package-lock.json yarn.lock roms/ @@ -8,4 +7,4 @@ data/emulator.min.js data/emulator.min.css data/cores .DS_Store -.vscode/* \ No newline at end of file +.vscode/* diff --git a/README.md b/README.md index ac4c3b61e..fb18555a3 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,12 @@ npm i npm start ``` +#### Minifying + +Before pushing the script files onto your production server it is recommended to minify them to save on load times as well as bandwidth. + +Read the [minifying](minify/README.md) documentation for more info. +
**>> When reporting bugs, please specify what version you are using** diff --git a/data/minify/index.js b/data/minify/index.js deleted file mode 100644 index afd81f37a..000000000 --- a/data/minify/index.js +++ /dev/null @@ -1,28 +0,0 @@ -const UglifyJS = require("uglify-js"); -const fs = require('fs'); -const uglifycss = require('uglifycss'); - -const scripts = [ - "emulator.js", - "nipplejs.js", - "shaders.js", - "storage.js", - "gamepad.js", - "GameManager.js", - "socket.io.min.js", - "compression.js" -]; -let code = "(function() {\n"; -for (let i=0; i -server it is recommended to minify them to save on
-load times as well as bandwidth. +Before pushing the script files onto your production server it is recommended to minify them to save on load times as well as bandwidth.
@@ -15,20 +12,19 @@ load times as well as bandwidth. ## Steps -1. Open a terminal in `/data/minify` . +1. Open a terminal in the root of the project. 2. Install the dependencies with: - ```sh - npm install - ``` + ```sh + npm i + ``` 3. Start the minification with: - ```sh - node index.js - ``` - + ```sh + node run minify + ``` diff --git a/minify/minify.js b/minify/minify.js new file mode 100644 index 000000000..b8cb09cf6 --- /dev/null +++ b/minify/minify.js @@ -0,0 +1,38 @@ +import path from "path"; +import { fileURLToPath } from "url"; +import minify from "@node-minify/core"; +import terser from "@node-minify/terser"; +import cleanCSS from "@node-minify/clean-css"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const rootPath = path.resolve(__dirname, "../"); + +async function doMinify() { + await minify({ + compressor: terser, + input: path.join(rootPath, "data/src/*.js"), + output: path.join(rootPath, "data/emulator.min.js"), + }) + .catch(function (err) { + console.error(err); + }) + .then(function () { + console.log("Minified JS"); + }); + await minify({ + compressor: cleanCSS, + input: path.join(rootPath, "data/emulator.css"), + output: path.join(rootPath, "data/emulator.min.css"), + }) + .catch(function (err) { + console.error(err); + }) + .then(function () { + console.log("Minified CSS"); + }); +} + +console.log("Minifying"); +await doMinify(); +console.log("Minifying Done!"); diff --git a/package.json b/package.json index d7e58c1f2..9e16e6c44 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,25 @@ { - "name": "@emulatorjs/emulatorjs", - "version": "4.2.1", - "repository": { - "type": "git", - "url": "https://github.com/EmulatorJS/EmulatorJS.git" - }, - "license": "GPL-3.0", - "description": "EmulatorJS is a frontend for RetroArch in the web browser.", - "scripts": { - "start": "http-server" - }, - "dependencies": { - "http-server": "^14.1.1" - } + "name": "@emulatorjs/emulatorjs", + "version": "4.2.1", + "type": "module", + "description": "EmulatorJS is a frontend for RetroArch in the web browser.", + "homepage": "https://emulatorjs.org", + "license": "GPL-3.0", + "repository": { + "type": "git", + "url": "https://github.com/EmulatorJS/EmulatorJS.git" + }, + "bugs": { + "url": "https://github.com/EmulatorJS/EmulatorJS/issues" + }, + "scripts": { + "start": "http-server", + "minify": "node minify/minify.js" + }, + "dependencies": { + "@node-minify/clean-css": "^9.0.1", + "@node-minify/core": "^9.0.2", + "@node-minify/terser": "^9.0.1", + "http-server": "^14.1.1" + } } From 8dbf1cff2977872f80794d990011c1cb5b46d211 Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:07:15 +1000 Subject: [PATCH 012/106] How to disable the formatter in VSCode (#994) * How to disable the formatter in VSCode * Fix incorrect markdown usage --- CONTRIBUTING.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0c885e80..d12f141ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,3 +9,21 @@ There are several ways to contribute, be it directly to helping develop features Just wanna donate? That'd help too! [libretro](https://retroarch.com/index.php?page=donate) [EmulatorJS](https://www.patreon.com/EmulatorJS) + +## Attention Visual Studio Code Users + +By default Visual Studio Code will apply formatting that is not consistent with the standard formatting used by this project. + +Please disable the formatter *before* submitting a Pull Request. + +The formatter can be disabled for this repo only (without affecting your own preferences) by creating a new file called `.vscode/settings.json` (if it doesn't already exist). + +Add the following options to the settings.json file to disable the formatter while working on this repo: +```json +{ + "diffEditor.ignoreTrimWhitespace": false, + "editor.formatOnPaste": false, + "editor.formatOnSave": false, + "editor.formatOnSaveMode": "modifications" +} +``` From bd26e754d93dbea42f37186e42e73d304830254b Mon Sep 17 00:00:00 2001 From: Allan Niles Date: Wed, 23 Apr 2025 17:08:30 -0600 Subject: [PATCH 013/106] Add build script (#989) * make build script * fix formating * Add newline * test action * debug * tag logic * that would do it * GITHUB_ENV * NODE_AUTH_TOKEN * update version * change order * setup cores * it works now --- .github/workflows/latest.yml | 2 +- .github/workflows/npm.yml | 51 +++++++++- .gitignore | 11 ++- .npmignore | 15 +++ build.js | 183 +++++++++++++++++++++++++++++++++++ data/cores/.npmignore | 3 + data/cores/README.md | 22 +++++ data/cores/core-README.md | 25 +++++ data/cores/package.json | 65 +++++++++++++ package.json | 13 ++- 10 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 .npmignore create mode 100644 build.js create mode 100644 data/cores/.npmignore create mode 100644 data/cores/README.md create mode 100644 data/cores/core-README.md create mode 100644 data/cores/package.json diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index 8405deb02..bcc8ba628 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -34,5 +34,5 @@ jobs: - name: Clean Up Minify run: | cd /mnt/HDD/public/.EmulatorJS/ - rm "minify/package-lock.json" + rm -f "minify/package-lock.json" rm -rf "minify/node_modules/" \ No newline at end of file diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 7165a6a79..50bbc84ed 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -13,8 +13,55 @@ jobs: with: node-version: '20.x' registry-url: 'https://registry.npmjs.org' + - name: Get latest release tag (if workflow_dispatch) + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + LATEST_TAG=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name') + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + - name: Download cores + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG_NAME=${{ github.event.release.tag_name || env.LATEST_TAG }} + TRIMMED_TAG=${TAG_NAME#v} + echo "TRIMMED_TAG=$TRIMMED_TAG" >> $GITHUB_ENV + gh release download "$TAG_NAME" \ + --repo ${{ github.repository }} \ + --pattern "$TRIMMED_TAG.7z" + - name: Extract cores + run: | + 7z x -y "$TRIMMED_TAG.7z" "data/cores/*" -o./ + rm "$TRIMMED_TAG.7z" - run: npm i - - run: npm ci - - run: npm publish --access public + - name: Make @emulatorjs/emulatorjs + run: | + node build.js --npm=emulatorjs + npm ci + npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Get cores list + run: | + echo "CORES=$(node build.js --npm=get-cores | jq -r '. | join(" ")')" >> $GITHUB_ENV + - name: Setup cores + run: | + node build.js --npm=cores + - name: Publish each core + run: | + cd data/cores + for core in $CORES; do + echo "Processing core: $core" + cd $core + npm publish --access public + cd .. + done + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Make @emulatorjs/cores + run: | + cd data/cores + npm i + npm ci + npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index e7c875191..3b86629bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ -**/node_modules/ -*.db +node_modules/ package-lock.json yarn.lock roms/ data/emulator.min.js data/emulator.min.css -data/cores +data/cores/* +!data/cores/README.md +!data/cores/core-README.md +!data/cores/package.json +!data/cores/.npmignore .DS_Store .vscode/* +*.tgz +dist/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..020a8469f --- /dev/null +++ b/.npmignore @@ -0,0 +1,15 @@ +.git +dist/ +node_modules/ +package-lock.json +.github/ +*.tgz +update.js +build.js +data/localization/translate.html +data/cores/* +!data/cores/README.md +!data/cores/package.json +minify/ +.npmignore +.gitignore diff --git a/build.js b/build.js new file mode 100644 index 000000000..36ebc4aed --- /dev/null +++ b/build.js @@ -0,0 +1,183 @@ +import fs from 'fs'; +import path from 'path'; +import Seven from 'node-7z'; +let version; + +try { + const packageJsonPath = path.resolve('package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + version = packageJson.version; +} catch (error) { + console.error("Error reading version from package.json:", error.message); + process.exit(1); +} + +const args = process.argv.slice(2); +const npmArg = args.find(arg => arg.startsWith('--npm=')); +const build_type = npmArg ? npmArg.split('=')[1] : process.env.npm; + +if (!build_type) { + const progressData = { + '7z': 0, + 'zip': 0 + }; + + const progressInterval = setInterval(() => { + process.stdout.clearLine(); + process.stdout.cursorTo(0); + if (progressData['7z'] < 100 && progressData['zip'] < 100) { + process.stdout.write(`7z Progress: ${progressData['7z']}% | Zip Progress: ${progressData['zip']}%`); + } else if (progressData['7z'] === 100) { + console.log(`${version}.7z created successfully!`); + process.stdout.write(`Zip Progress: ${progressData['zip']}%`); + progressData['7z'] = 101; + } else if (progressData['zip'] === 100) { + console.log(`${version}.zip created successfully!`); + process.stdout.write(`7z Progress: ${progressData['7z']}%`); + progressData['zip'] = 101; + } else if (progressData['zip'] >= 100 && progressData['7z'] >= 100) { + process.stdout.write(`All archives for EmulatorJS version: ${version} created successfully!`); + clearInterval(progressInterval); + console.log('\nArchives are in the dist/ folder.'); + } else if (progressData['7z'] >= 100) { + process.stdout.write(`Zip Progress: ${progressData['zip']}%`); + } else if (progressData['zip'] >= 100) { + process.stdout.write(`7z Progress: ${progressData['7z']}%`); + } + }, 100); + + console.log(`Creating archives for EmulatorJS version: ${version}`); + + const npmIgnorePath = path.resolve('.npmignore'); + if (!fs.existsSync('dist')) { + fs.mkdirSync('dist'); + } + const distNpmIgnorePath = path.resolve('dist', '.ignore'); + fs.copyFileSync(npmIgnorePath, distNpmIgnorePath); + const npmIgnoreContent = fs.readFileSync(npmIgnorePath, 'utf8'); + const updatedNpmIgnoreContent = npmIgnoreContent.replace('data/cores/*', 'data/cores/core-README.md\ndata/cores/package.json'); + fs.writeFileSync(distNpmIgnorePath, updatedNpmIgnoreContent, 'utf8'); + + Seven.add(`dist/${version}.7z`, './', { + $raw: ['-xr@dist/.ignore'], + $progress: true + }).on('progress', function (progress) { + progressData['7z'] = progress.percent; + }).on('end', function () { + progressData['7z'] = 100; + + }); + + Seven.add(`dist/${version}.zip`, './', { + $raw: ['-xr@dist/.ignore'], + $progress: true + }).on('progress', function (progress) { + progressData['zip'] = progress.percent; + }).on('end', function () { + progressData['zip'] = 100; + }); +} else if (build_type !== "emulatorjs" && build_type !== "cores" && build_type !== "get-cores") { + console.log("Invalid argument. Use --npm=emulatorjs, --npm=cores or --npm=get-cores."); + process.exit(1); +} else { + const removeLogo = () => { + const readmePath = path.resolve('README.md'); + const readmeContent = fs.readFileSync(readmePath, 'utf8'); + const updatedContent = readmeContent + .split('\n') + .filter(line => !line.includes('docs/Logo-light.png#gh-dark-mode-only>')) + .join('\n'); + fs.writeFileSync(readmePath, updatedContent, 'utf8'); + }; + + const getCores = async () => { + const coresJsonPath = path.resolve('data', 'cores', 'cores.json'); + if (!fs.existsSync(coresJsonPath)) { + console.error(`Cores JSON file not found at ${coresJsonPath}`); + return; + } + return JSON.parse(fs.readFileSync(coresJsonPath, 'utf8')); + }; + + if (build_type === "emulatorjs") { + console.log(`Current EmulatorJS Version: ${version}`); + removeLogo(); + console.log("Ready to build EmulatorJS!"); + } else if (build_type === "get-cores") { + const cores = await getCores(); + console.log(JSON.stringify(cores.map(coreName => coreName.name))); + } else if (build_type === "cores") { + console.log(`Current EmulatorJS Version: ${version}`); + console.log("Building cores..."); + const allCores = await getCores(); + console.log("Building EmulatorJS cores:"); + const coreNames = allCores.map(coreName => coreName.name); + console.log(coreNames.join(', ')); + if (!coreNames) { + console.error("No cores found."); + process.exit(1); + } + const coresPath = path.resolve('data', 'cores'); + const coresFiles = fs.readdirSync(coresPath); + const dataFiles = coresFiles.filter(file => file.endsWith('.data') || file.endsWith('.zip')); + const cores = {}; + for (const core of coreNames) { + const coreFiles = dataFiles.filter(file => file.startsWith(core + '-')); + if (!cores[core]) { + cores[core] = []; + } + cores[core].push(...coreFiles); + } + const packagePath = path.resolve('data', 'cores', 'package.json'); + const packageContent = fs.readFileSync(packagePath, 'utf8'); + const packageJson = JSON.parse(packageContent); + packageJson.dependencies = { + "@emulatorjs/emulatorjs": "latest" + }; + fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 4), 'utf8'); + for (const core in cores) { + if (!fs.existsSync(path.resolve('data', 'cores', core))) { + fs.mkdirSync(path.resolve('data', 'cores', core)); + } + for (const file of cores[core]) { + const sourcePath = path.resolve('data', 'cores', file); + const destPath = path.resolve('data', 'cores', core, file); + fs.copyFileSync(sourcePath, destPath); + const reportsPath = path.resolve('data', 'cores', core, 'reports'); + if (!fs.existsSync(reportsPath)) { + fs.mkdirSync(reportsPath); + } + } + const coreReportPath = path.resolve('data', 'cores', 'reports', `${core}.json`); + const coreReportDestPath = path.resolve('data', 'cores', core, 'reports', `${core}.json`); + fs.copyFileSync(coreReportPath, coreReportDestPath); + + const corePackagePath = path.resolve('data', 'cores', 'package.json'); + const corePackageDestPath = path.resolve('data', 'cores', core, 'package.json'); + const corePackageContent = fs.readFileSync(corePackagePath, 'utf8'); + const corePackageJson = JSON.parse(corePackageContent); + corePackageJson.name = `@emulatorjs/core-${core}`; + corePackageJson.description = `EmulatorJS Core: ${core}`; + corePackageJson.license = allCores.find(c => c.name === core).license; + corePackageJson.repository.url = allCores.find(c => c.name === core).repo + '.git'; + corePackageJson.bugs.url = allCores.find(c => c.name === core).repo + '/issues'; + corePackageJson.dependencies = { + "@emulatorjs/emulatorjs": "latest" + }; + fs.writeFileSync(corePackageDestPath, JSON.stringify(corePackageJson, null, 4), 'utf8'); + + const coreReadmePath = path.resolve('data', 'cores', 'core-README.md'); + const coreReadmeDestPath = path.resolve('data', 'cores', core, 'README.md'); + const coreReadmeContent = fs.readFileSync(coreReadmePath, 'utf8'); + const updatedCoreReadmeContent = coreReadmeContent + .replace(//g, `${core}`) + .replace(//g, allCores.find(c => c.name === core).repo); + fs.writeFileSync(coreReadmeDestPath, updatedCoreReadmeContent, 'utf8'); + + packageJson.dependencies[`@emulatorjs/core-${core}`] = "latest"; + fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 4), 'utf8'); + } + console.log("EmulatorJS cores built successfully!"); + console.log("Ready to build EmulatorJS!"); + } +} diff --git a/data/cores/.npmignore b/data/cores/.npmignore new file mode 100644 index 000000000..0cd7144e2 --- /dev/null +++ b/data/cores/.npmignore @@ -0,0 +1,3 @@ +* +!README.md +!package.json diff --git a/data/cores/README.md b/data/cores/README.md new file mode 100644 index 000000000..f95fa406a --- /dev/null +++ b/data/cores/README.md @@ -0,0 +1,22 @@ +# EmulatorJS Cores + +This package contains the stable cores for EmulatorJS. + +Lean more about EmulatorJS at https://emulatorjs.org + +Cores are build using this repository: +https://github.com/EmulatorJS/build + +## How to install + +To install all cores, run the following command: + +```bash +npm install @emulatorjs/cores +``` +To install a specific core, run the following command: + +```bash +npm install @emulatorjs/core- +``` + diff --git a/data/cores/core-README.md b/data/cores/core-README.md new file mode 100644 index 000000000..634618ca9 --- /dev/null +++ b/data/cores/core-README.md @@ -0,0 +1,25 @@ +# EmulatorJS Core: + +This package contains the stable EmulatorJS core: + +Lean more about EmulatorJS at https://emulatorjs.org + +Core repository: + + +Core is build using this repository: +https://github.com/EmulatorJS/build + +## How to install + +To install core, run the following command: + +```bash +npm install @emulatorjs/core- +``` +To install all cores, run the following command: + +```bash +npm install @emulatorjs/cores +``` + diff --git a/data/cores/package.json b/data/cores/package.json new file mode 100644 index 000000000..da3f85cbc --- /dev/null +++ b/data/cores/package.json @@ -0,0 +1,65 @@ +{ + "name": "@emulatorjs/cores", + "version": "4.2.1", + "type": "module", + "description": "EmulatorJS Cores", + "homepage": "https://emulatorjs.org", + "license": "GPL-3.0", + "repository": { + "type": "git", + "url": "https://github.com/EmulatorJS/EmulatorJS.git" + }, + "bugs": { + "url": "https://github.com/EmulatorJS/EmulatorJS/issues" + }, + "sideEffects": true, + "dependencies": { + "@emulatorjs/emulatorjs": "latest", + "@emulatorjs/core-81": "latest", + "@emulatorjs/core-mame2003": "latest", + "@emulatorjs/core-vice_x64": "latest", + "@emulatorjs/core-vice_x64sc": "latest", + "@emulatorjs/core-vice_x128": "latest", + "@emulatorjs/core-vice_xpet": "latest", + "@emulatorjs/core-vice_xplus4": "latest", + "@emulatorjs/core-vice_xvic": "latest", + "@emulatorjs/core-fceumm": "latest", + "@emulatorjs/core-nestopia": "latest", + "@emulatorjs/core-snes9x": "latest", + "@emulatorjs/core-gambatte": "latest", + "@emulatorjs/core-mgba": "latest", + "@emulatorjs/core-beetle_vb": "latest", + "@emulatorjs/core-mupen64plus_next": "latest", + "@emulatorjs/core-melonds": "latest", + "@emulatorjs/core-desmume2015": "latest", + "@emulatorjs/core-desmume": "latest", + "@emulatorjs/core-a5200": "latest", + "@emulatorjs/core-fbalpha2012_cps1": "latest", + "@emulatorjs/core-fbalpha2012_cps2": "latest", + "@emulatorjs/core-prosystem": "latest", + "@emulatorjs/core-stella2014": "latest", + "@emulatorjs/core-opera": "latest", + "@emulatorjs/core-genesis_plus_gx": "latest", + "@emulatorjs/core-yabause": "latest", + "@emulatorjs/core-handy": "latest", + "@emulatorjs/core-virtualjaguar": "latest", + "@emulatorjs/core-pcsx_rearmed": "latest", + "@emulatorjs/core-picodrive": "latest", + "@emulatorjs/core-fbneo": "latest", + "@emulatorjs/core-mednafen_psx_hw": "latest", + "@emulatorjs/core-mednafen_pce": "latest", + "@emulatorjs/core-mednafen_pcfx": "latest", + "@emulatorjs/core-mednafen_ngp": "latest", + "@emulatorjs/core-mednafen_wswan": "latest", + "@emulatorjs/core-gearcoleco": "latest", + "@emulatorjs/core-parallel_n64": "latest", + "@emulatorjs/core-mame2003_plus": "latest", + "@emulatorjs/core-puae": "latest", + "@emulatorjs/core-smsplus": "latest", + "@emulatorjs/core-fuse": "latest", + "@emulatorjs/core-cap32": "latest", + "@emulatorjs/core-crocods": "latest", + "@emulatorjs/core-prboom": "latest", + "@emulatorjs/core-ppsspp": "latest" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 9e16e6c44..cd3e08774 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@emulatorjs/emulatorjs", - "version": "4.2.1", + "version": "4.2.1-beta.2", "type": "module", "description": "EmulatorJS is a frontend for RetroArch in the web browser.", "homepage": "https://emulatorjs.org", "license": "GPL-3.0", + "sideEffects": true, "repository": { "type": "git", "url": "https://github.com/EmulatorJS/EmulatorJS.git" @@ -14,12 +15,18 @@ }, "scripts": { "start": "http-server", - "minify": "node minify/minify.js" + "minify": "node minify/minify.js", + "build": "node build.js" }, "dependencies": { + "http-server": "^14.1.1", "@node-minify/clean-css": "^9.0.1", "@node-minify/core": "^9.0.2", "@node-minify/terser": "^9.0.1", - "http-server": "^14.1.1" + "node-7z": "^3.0.0", + "node-fetch": "^3.3.2" + }, + "optionalDependencies": { + "@emulatorjs/cores": "latest" } } From 443a6f81cd182a010b353540d33b3a63f917afac Mon Sep 17 00:00:00 2001 From: Ethan O'Brien <77750390+ethanaobrien@users.noreply.github.com> Date: Sun, 27 Apr 2025 22:45:49 -0500 Subject: [PATCH 014/106] Correct n64 virtual gamepad mapping I hope --- data/src/emulator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/src/emulator.js b/data/src/emulator.js index b415bce2e..9e8795225 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -3222,7 +3222,7 @@ class EmulatorJS { {"type":"button","text":"B","id":"b","location":"right","left":-10,"top":95,"input_value":1,"bold":true}, {"type":"button","text":"A","id":"a","location":"right","left":40,"top":150,"input_value":0,"bold":true}, {"type":"zone","id":"stick","location":"left","left":"50%","top":"100%","joystickInput":true,"inputValues":[16, 17, 18, 19]}, - {"type":"zone","id":"dpad","location":"left","left":"50%","top":"0%","joystickInput":false,"inputValues":[4,5,6,7]}, + {"type":"zone","id":"dpad","location":"left","left":"50%","top":"0%","joystickInput":true,"inputValues":[20, 21, 22, 23]}, {"type":"button","text":"Start","id":"start","location":"center","left":30,"top":-10,"fontSize":15,"block":true,"input_value":3}, {"type":"button","text":"L","id":"l","block":true,"location":"top","left":10,"top":-40,"bold":true,"input_value":10}, {"type":"button","text":"R","id":"r","block":true,"location":"top","right":10,"top":-40,"bold":true,"input_value":11}, From e28e350235bb11afbfa8a54da57eb770b8827169 Mon Sep 17 00:00:00 2001 From: Allan Niles Date: Mon, 28 Apr 2025 10:13:31 -0600 Subject: [PATCH 015/106] Update localizations (#987) * update localizations * add localization docs * move tool * fix wording --- README.md | 9 +- data/localization/{readme.md => README.md} | 8 +- data/localization/Translate.html | 529 --------- data/localization/en-US.json | 336 ++++++ data/localization/en.json | 310 ----- data/localization/retroarch.json | 1232 ++++++++++---------- 6 files changed, 965 insertions(+), 1459 deletions(-) rename data/localization/{readme.md => README.md} (80%) delete mode 100644 data/localization/Translate.html create mode 100644 data/localization/en-US.json delete mode 100644 data/localization/en.json diff --git a/README.md b/README.md index fb18555a3..0b00d14d0 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,6 @@ Or the Matrix server (#emulatorjs:matrix.emulatorjs.org):
- ### Issues *If something doesn't work, please consider opening an* ***[Issue]***
@@ -88,6 +87,8 @@ npm i npm start ``` +
+ #### Minifying Before pushing the script files onto your production server it is recommended to minify them to save on load times as well as bandwidth. @@ -96,6 +97,12 @@ Read the [minifying](minify/README.md) documentation for more info.
+#### Localization + +If you want to help with localization, please check out the [localization](data/localization/README.md) documentation. + +
+ **>> When reporting bugs, please specify what version you are using**
diff --git a/data/localization/readme.md b/data/localization/README.md similarity index 80% rename from data/localization/readme.md rename to data/localization/README.md index 96c705bb8..399bb3c71 100644 --- a/data/localization/readme.md +++ b/data/localization/README.md @@ -48,13 +48,15 @@ Translated for `hi-HI`, `ar-AR`, `jv-JV`, `ben-BEN`, `ru-RU`, `de-GER`, `ko-KO` ## Contributing -Download the default `en.json` file and simply translate all the words that start with the `-` (remove the dash afterwards) then perform a pull request or open an issue with the file uploaded and I will add your work. +To contribute, please download the default `en-US.json` language file to use as a template, translate the strings and then submit the file with a Pull Request or Issue. + +The EmulatorJS team will review and add your changes. The `retroarch.json` are all the setting names for the menu. They will default to english if not found. You can set `EJS_settingsLanguage` to `true` to see the missing retroarch settings names for the current language. You can translate them and add the to the language file. -The control maping traslations for controllers are diffrent for each controller. They will need to be added to the language file if they are not in the default `en.json` file. +The control mapping translations for controllers are diffrent for each controller. They will need to be added to the language file if they are not in the default `en-US.json` file. -You can also use the [Translation Helper](Translate.html) tool to help you translate the file. +You can also use the [Translation Helper](https://emulatorjs.org/translate) tool to help you translate the file. Please contribute!! diff --git a/data/localization/Translate.html b/data/localization/Translate.html deleted file mode 100644 index 0970d6a1d..000000000 --- a/data/localization/Translate.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - - EmulatorJS | Translate Languages - - - - -
-

Translate Languages

- -
-
-
-
- - -
-
- - -
-
- - -
-
- - - - - diff --git a/data/localization/en-US.json b/data/localization/en-US.json new file mode 100644 index 000000000..726b418b6 --- /dev/null +++ b/data/localization/en-US.json @@ -0,0 +1,336 @@ +{ + "0": "0", + "1": "1", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7", + "8": "8", + "9": "9", + "Restart": "Restart", + "Pause": "Pause", + "Play": "Play", + "Save State": "Save State", + "Load State": "Load State", + "Control Settings": "Control Settings", + "Cheats": "Cheats", + "Cache Manager": "Cache Manager", + "Export Save File": "Export Save File", + "Import Save File": "Import Save File", + "Netplay": "Netplay", + "Mute": "Mute", + "Unmute": "Unmute", + "Settings": "Settings", + "Enter Fullscreen": "Enter Fullscreen", + "Exit Fullscreen": "Exit Fullscreen", + "Context Menu": "Context Menu", + "Reset": "Reset", + "Clear": "Clear", + "Close": "Close", + "QUICK SAVE STATE": "QUICK SAVE STATE", + "QUICK LOAD STATE": "QUICK LOAD STATE", + "CHANGE STATE SLOT": "CHANGE STATE SLOT", + "FAST FORWARD": "FAST FORWARD", + "Player": "Player", + "Connected Gamepad": "Connected Gamepad", + "Gamepad": "Gamepad", + "Keyboard": "Keyboard", + "Set": "Set", + "Add Cheat": "Add Cheat", + "Note that some cheats require a restart to disable": "Note that some cheats require a restart to disable", + "Create a Room": "Create a Room", + "Rooms": "Rooms", + "Start Game": "Start Game", + "Click to resume Emulator": "Click to resume Emulator", + "Drop save state here to load": "Drop save state here to load", + "Loading...": "Loading...", + "Download Game Core": "Download Game Core", + "Outdated graphics driver": "Outdated graphics driver", + "Decompress Game Core": "Decompress Game Core", + "Download Game Data": "Download Game Data", + "Decompress Game Data": "Decompress Game Data", + "Shaders": "Shaders", + "Disabled": "Disabled", + "2xScaleHQ": "2xScaleHQ", + "4xScaleHQ": "4xScaleHQ", + "CRT easymode": "CRT easymode", + "CRT aperture": "CRT aperture", + "CRT geom": "CRT geom", + "CRT mattias": "CRT mattias", + "FPS": "FPS", + "show": "show", + "hide": "hide", + "Fast Forward Ratio": "Fast Forward Ratio", + "Fast Forward": "Fast Forward", + "Enabled": "Enabled", + "Save State Slot": "Save State Slot", + "Save State Location": "Save State Location", + "Download": "Download", + "Keep in Browser": "Keep in Browser", + "Auto": "Auto", + "NTSC": "NTSC", + "PAL": "PAL", + "Dendy": "Dendy", + "8:7 PAR": "8:7 PAR", + "4:3": "4:3", + "Low": "Low", + "High": "High", + "Very High": "Very High", + "None": "None", + "Player 1": "Player 1", + "Player 2": "Player 2", + "Both": "Both", + "SAVED STATE TO SLOT": "SAVED STATE TO SLOT", + "LOADED STATE FROM SLOT": "LOADED STATE FROM SLOT", + "SET SAVE STATE SLOT TO": "SET SAVE STATE SLOT TO", + "Network Error": "Network Error", + "Submit": "Submit", + "Description": "Description", + "Code": "Code", + "Add Cheat Code": "Add Cheat Code", + "Leave Room": "Leave Room", + "Password": "Password", + "Password (optional)": "Password (optional)", + "Max Players": "Max Players", + "Room Name": "Room Name", + "Join": "Join", + "Player Name": "Player Name", + "Set Player Name": "Set Player Name", + "Left Handed Mode": "Left Handed Mode", + "Virtual Gamepad": "Virtual Gamepad", + "Disk": "Disk", + "Press Keyboard": "Press Keyboard", + "INSERT COIN": "INSERT COIN", + "Remove": "Remove", + "SAVE LOADED FROM BROWSER": "SAVE LOADED FROM BROWSER", + "SAVE SAVED TO BROWSER": "SAVE SAVED TO BROWSER", + "Join the discord": "Join the discord", + "View on GitHub": "View on GitHub", + "Failed to start game": "Failed to start game", + "Download Game BIOS": "Download Game BIOS", + "Decompress Game BIOS": "Decompress Game BIOS", + "Download Game Parent": "Download Game Parent", + "Decompress Game Parent": "Decompress Game Parent", + "Download Game Patch": "Download Game Patch", + "Decompress Game Patch": "Decompress Game Patch", + "Download Game State": "Download Game State", + "Check console": "Check console", + "Error for site owner": "Error for site owner", + "EmulatorJS": "EmulatorJS", + "Clear All": "Clear All", + "Take Screenshot": "Take Screenshot", + "Start screen recording": "Start screen recording", + "Stop screen recording": "Stop screen recording", + "Quick Save": "Quick Save", + "Quick Load": "Quick Load", + "REWIND": "REWIND", + "Rewind Enabled (requires restart)": "Rewind Enabled (requires restart)", + "Rewind Granularity": "Rewind Granularity", + "Slow Motion Ratio": "Slow Motion Ratio", + "Slow Motion": "Slow Motion", + "Home": "Home", + "EmulatorJS License": "EmulatorJS License", + "RetroArch License": "RetroArch License", + "This project is powered by": "This project is powered by", + "View the RetroArch license here": "View the RetroArch license here", + "SLOW MOTION": "SLOW MOTION", + "A": "A", + "B": "B", + "SELECT": "SELECT", + "START": "START", + "UP": "UP", + "DOWN": "DOWN", + "LEFT": "LEFT", + "RIGHT": "RIGHT", + "X": "X", + "Y": "Y", + "L": "L", + "R": "R", + "Z": "Z", + "STICK UP": "STICK UP", + "STICK DOWN": "STICK DOWN", + "STICK LEFT": "STICK LEFT", + "STICK RIGHT": "STICK RIGHT", + "C-PAD UP": "C-PAD UP", + "C-PAD DOWN": "C-PAD DOWN", + "C-PAD LEFT": "C-PAD LEFT", + "C-PAD RIGHT": "C-PAD RIGHT", + "MICROPHONE": "MICROPHONE", + "BUTTON 1 / START": "BUTTON 1 / START", + "BUTTON 2": "BUTTON 2", + "BUTTON": "BUTTON", + "LEFT D-PAD UP": "LEFT D-PAD UP", + "LEFT D-PAD DOWN": "LEFT D-PAD DOWN", + "LEFT D-PAD LEFT": "LEFT D-PAD LEFT", + "LEFT D-PAD RIGHT": "LEFT D-PAD RIGHT", + "RIGHT D-PAD UP": "RIGHT D-PAD UP", + "RIGHT D-PAD DOWN": "RIGHT D-PAD DOWN", + "RIGHT D-PAD LEFT": "RIGHT D-PAD LEFT", + "RIGHT D-PAD RIGHT": "RIGHT D-PAD RIGHT", + "C": "C", + "MODE": "MODE", + "FIRE": "FIRE", + "RESET": "RESET", + "LEFT DIFFICULTY A": "LEFT DIFFICULTY A", + "LEFT DIFFICULTY B": "LEFT DIFFICULTY B", + "RIGHT DIFFICULTY A": "RIGHT DIFFICULTY A", + "RIGHT DIFFICULTY B": "RIGHT DIFFICULTY B", + "COLOR": "COLOR", + "B/W": "B/W", + "PAUSE": "PAUSE", + "OPTION": "OPTION", + "OPTION 1": "OPTION 1", + "OPTION 2": "OPTION 2", + "L2": "L2", + "R2": "R2", + "L3": "L3", + "R3": "R3", + "L STICK UP": "L STICK UP", + "L STICK DOWN": "L STICK DOWN", + "L STICK LEFT": "L STICK LEFT", + "L STICK RIGHT": "L STICK RIGHT", + "R STICK UP": "R STICK UP", + "R STICK DOWN": "R STICK DOWN", + "R STICK LEFT": "R STICK LEFT", + "R STICK RIGHT": "R STICK RIGHT", + "Start": "Start", + "Select": "Select", + "Fast": "Fast", + "Slow": "Slow", + "a": "a", + "b": "b", + "c": "c", + "d": "d", + "e": "e", + "f": "f", + "g": "g", + "h": "h", + "i": "i", + "j": "j", + "k": "k", + "l": "l", + "m": "m", + "n": "n", + "o": "o", + "p": "p", + "q": "q", + "r": "r", + "s": "s", + "t": "t", + "u": "u", + "v": "v", + "w": "w", + "x": "x", + "y": "y", + "z": "z", + "enter": "enter", + "escape": "escape", + "space": "space", + "tab": "tab", + "backspace": "backspace", + "delete": "delete", + "arrowup": "arrowup", + "arrowdown": "arrowdown", + "arrowleft": "arrowleft", + "arrowright": "arrowright", + "f1": "f1", + "f2": "f2", + "f3": "f3", + "f4": "f4", + "f5": "f5", + "f6": "f6", + "f7": "f7", + "f8": "f8", + "f9": "f9", + "f10": "f10", + "f11": "f11", + "f12": "f12", + "shift": "shift", + "control": "control", + "alt": "alt", + "meta": "meta", + "capslock": "capslock", + "insert": "insert", + "home": "home", + "end": "end", + "pageup": "pageup", + "pagedown": "pagedown", + "!": "!", + "@": "@", + "#": "#", + "$": "$", + "%": "%", + "^": "^", + "&": "&", + "*": "*", + "(": "(", + ")": ")", + "-": "-", + "_": "_", + "+": "+", + "=": "=", + "[": "[", + "]": "]", + "{": "{", + "}": "}", + ";": ";", + ":": ":", + "'": "'", + "\"": "\"", + ",": ",", + ".": ".", + "<": "<", + ">": ">", + "/": "/", + "?": "?", + "LEFT_STICK_X": "LEFT_STICK_X", + "LEFT_STICK_Y": "LEFT_STICK_Y", + "RIGHT_STICK_X": "RIGHT_STICK_X", + "RIGHT_STICK_Y": "RIGHT_STICK_Y", + "LEFT_TRIGGER": "LEFT_TRIGGER", + "RIGHT_TRIGGER": "RIGHT_TRIGGER", + "A_BUTTON": "A_BUTTON", + "B_BUTTON": "B_BUTTON", + "X_BUTTON": "X_BUTTON", + "Y_BUTTON": "Y_BUTTON", + "START_BUTTON": "START_BUTTON", + "SELECT_BUTTON": "SELECT_BUTTON", + "L1_BUTTON": "L1_BUTTON", + "R1_BUTTON": "R1_BUTTON", + "L2_BUTTON": "L2_BUTTON", + "R2_BUTTON": "R2_BUTTON", + "LEFT_THUMB_BUTTON": "LEFT_THUMB_BUTTON", + "RIGHT_THUMB_BUTTON": "RIGHT_THUMB_BUTTON", + "DPAD_UP": "DPAD_UP", + "DPAD_DOWN": "DPAD_DOWN", + "DPAD_LEFT": "DPAD_LEFT", + "DPAD_RIGHT": "DPAD_RIGHT", + "Disks": "Disks", + "Exit EmulatorJS": "Exit EmulatorJS", + "BUTTON_1": "BUTTON_1", + "BUTTON_2": "BUTTON_2", + "BUTTON_3": "BUTTON_3", + "BUTTON_4": "BUTTON_4", + "up arrow": "up arrow", + "down arrow": "down arrow", + "left arrow": "left arrow", + "right arrow": "right arrow", + "LEFT_TOP_SHOULDER": "LEFT_TOP_SHOULDER", + "RIGHT_TOP_SHOULDER": "RIGHT_TOP_SHOULDER", + "CRT beam": "CRT beam", + "CRT caligari": "CRT caligari", + "CRT lottes": "CRT lottes", + "CRT yeetron": "CRT yeetron", + "CRT zfast": "CRT zfast", + "SABR": "SABR", + "Bicubic": "Bicubic", + "Mix frames": "Mix frames", + "WebGL2": "WebGL2", + "Requires restart": "Requires restart", + "VSync": "VSync", + "Video Rotation": "Video Rotation", + "Rewind Enabled (Requires restart)": "Rewind Enabled (Requires restart)", + "System Save interval": "System Save interval" +} diff --git a/data/localization/en.json b/data/localization/en.json deleted file mode 100644 index 50333f514..000000000 --- a/data/localization/en.json +++ /dev/null @@ -1,310 +0,0 @@ -{ - "0": "-0", - "1": "-1", - "2": "-2", - "3": "-3", - "4": "-4", - "5": "-5", - "6": "-6", - "7": "-7", - "8": "-8", - "9": "-9", - "Restart": "-Restart", - "Pause": "-Pause", - "Play": "-Play", - "Save State": "-Save State", - "Load State": "-Load State", - "Control Settings": "-Control Settings", - "Cheats": "-Cheats", - "Cache Manager": "-Cache Manager", - "Export Save File": "-Export Save File", - "Import Save File": "-Import Save File", - "Netplay": "-Netplay", - "Mute": "-Mute", - "Unmute": "-Unmute", - "Settings": "-Settings", - "Enter Fullscreen": "-Enter Fullscreen", - "Exit Fullscreen": "-Exit Fullscreen", - "Context Menu": "-Context Menu", - "Reset": "-Reset", - "Clear": "-Clear", - "Close": "-Close", - "QUICK SAVE STATE": "-QUICK SAVE STATE", - "QUICK LOAD STATE": "-QUICK LOAD STATE", - "CHANGE STATE SLOT": "-CHANGE STATE SLOT", - "FAST FORWARD": "-FAST FORWARD", - "Player": "-Player", - "Connected Gamepad": "-Connected Gamepad", - "Gamepad": "-Gamepad", - "Keyboard": "-Keyboard", - "Set": "-Set", - "Add Cheat": "-Add Cheat", - "Note that some cheats require a restart to disable": "-Note that some cheats require a restart to disable", - "Create a Room": "-Create a Room", - "Rooms": "-Rooms", - "Start Game": "-Start Game", - "Click to resume Emulator": "-Click to resume Emulator", - "Drop save state here to load": "-Drop save state here to load", - "Loading...": "-Loading...", - "Download Game Core": "-Download Game Core", - "Outdated graphics driver": "-Outdated graphics driver", - "Decompress Game Core": "-Decompress Game Core", - "Download Game Data": "-Download Game Data", - "Decompress Game Data": "-Decompress Game Data", - "Shaders": "-Shaders", - "Disabled": "-Disabled", - "2xScaleHQ": "-2xScaleHQ", - "4xScaleHQ": "-4xScaleHQ", - "CRT easymode": "-CRT easymode", - "CRT aperture": "-CRT aperture", - "CRT geom": "-CRT geom", - "CRT mattias": "-CRT mattias", - "FPS": "-FPS", - "show": "-show", - "hide": "-hide", - "Fast Forward Ratio": "-Fast Forward Ratio", - "Fast Forward": "-Fast Forward", - "Enabled": "-Enabled", - "Save State Slot": "-Save State Slot", - "Save State Location": "-Save State Location", - "Download": "-Download", - "Keep in Browser": "-Keep in Browser", - "Auto": "-Auto", - "NTSC": "-NTSC", - "PAL": "-PAL", - "Dendy": "-Dendy", - "8:7 PAR": "-8:7 PAR", - "4:3": "-4:3", - "Low": "-Low", - "High": "-High", - "Very High": "-Very High", - "None": "-None", - "Player 1": "-Player 1", - "Player 2": "-Player 2", - "Both": "-Both", - "SAVED STATE TO SLOT": "-SAVED STATE TO SLOT", - "LOADED STATE FROM SLOT": "-LOADED STATE FROM SLOT", - "SET SAVE STATE SLOT TO": "-SET SAVE STATE SLOT TO", - "Network Error": "-Network Error", - "Submit": "-Submit", - "Description": "-Description", - "Code": "-Code", - "Add Cheat Code": "-Add Cheat Code", - "Leave Room": "-Leave Room", - "Password": "-Password", - "Password (optional)": "-Password (optional)", - "Max Players": "-Max Players", - "Room Name": "-Room Name", - "Join": "-Join", - "Player Name": "-Player Name", - "Set Player Name": "-Set Player Name", - "Left Handed Mode": "-Left Handed Mode", - "Virtual Gamepad": "-Virtual Gamepad", - "Disk": "-Disk", - "Press Keyboard": "-Press Keyboard", - "INSERT COIN": "-INSERT COIN", - "Remove": "-Remove", - "SAVE LOADED FROM BROWSER": "-SAVE LOADED FROM BROWSER", - "SAVE SAVED TO BROWSER": "-SAVE SAVED TO BROWSER", - "Join the discord": "-Join the discord", - "View on GitHub": "-View on GitHub", - "Failed to start game": "-Failed to start game", - "Download Game BIOS": "-Download Game BIOS", - "Decompress Game BIOS": "-Decompress Game BIOS", - "Download Game Parent": "-Download Game Parent", - "Decompress Game Parent": "-Decompress Game Parent", - "Download Game Patch": "-Download Game Patch", - "Decompress Game Patch": "-Decompress Game Patch", - "Download Game State": "-Download Game State", - "Check console": "-Check console", - "Error for site owner": "-Error for site owner", - "EmulatorJS": "-EmulatorJS", - "Clear All": "-Clear All", - "Take Screenshot": "-Take Screenshot", - "Start screen recording": "-Start screen recording", - "Stop screen recording": "-Stop screen recording", - "Quick Save": "-Quick Save", - "Quick Load": "-Quick Load", - "REWIND": "-REWIND", - "Rewind Enabled (requires restart)": "-Rewind Enabled (requires restart)", - "Rewind Granularity": "-Rewind Granularity", - "Slow Motion Ratio": "-Slow Motion Ratio", - "Slow Motion": "-Slow Motion", - "Home": "-Home", - "EmulatorJS License": "-EmulatorJS License", - "RetroArch License": "-RetroArch License", - "This project is powered by": "-This project is powered by", - "View the RetroArch license here": "-View the RetroArch license here", - "SLOW MOTION": "-SLOW MOTION", - "A": "-A", - "B": "-B", - "SELECT": "-SELECT", - "START": "-START", - "UP": "-UP", - "DOWN": "-DOWN", - "LEFT": "-LEFT", - "RIGHT": "-RIGHT", - "X": "-X", - "Y": "-Y", - "L": "-L", - "R": "-R", - "Z": "-Z", - "STICK UP": "-STICK UP", - "STICK DOWN": "-STICK DOWN", - "STICK LEFT": "-STICK LEFT", - "STICK RIGHT": "-STICK RIGHT", - "C-PAD UP": "-C-PAD UP", - "C-PAD DOWN": "-C-PAD DOWN", - "C-PAD LEFT": "-C-PAD LEFT", - "C-PAD RIGHT": "-C-PAD RIGHT", - "MICROPHONE": "-MICROPHONE", - "BUTTON 1 / START": "-BUTTON 1 / START", - "BUTTON 2": "-BUTTON 2", - "BUTTON": "-BUTTON", - "LEFT D-PAD UP": "-LEFT D-PAD UP", - "LEFT D-PAD DOWN": "-LEFT D-PAD DOWN", - "LEFT D-PAD LEFT": "-LEFT D-PAD LEFT", - "LEFT D-PAD RIGHT": "-LEFT D-PAD RIGHT", - "RIGHT D-PAD UP": "-RIGHT D-PAD UP", - "RIGHT D-PAD DOWN": "-RIGHT D-PAD DOWN", - "RIGHT D-PAD LEFT": "-RIGHT D-PAD LEFT", - "RIGHT D-PAD RIGHT": "-RIGHT D-PAD RIGHT", - "C": "-C", - "MODE": "-MODE", - "FIRE": "-FIRE", - "RESET": "-RESET", - "LEFT DIFFICULTY A": "-LEFT DIFFICULTY A", - "LEFT DIFFICULTY B": "-LEFT DIFFICULTY B", - "RIGHT DIFFICULTY A": "-RIGHT DIFFICULTY A", - "RIGHT DIFFICULTY B": "-RIGHT DIFFICULTY B", - "COLOR": "-COLOR", - "B/W": "-B/W", - "PAUSE": "-PAUSE", - "OPTION": "-OPTION", - "OPTION 1": "-OPTION 1", - "OPTION 2": "-OPTION 2", - "L2": "-L2", - "R2": "-R2", - "L3": "-L3", - "R3": "-R3", - "L STICK UP": "-L STICK UP", - "L STICK DOWN": "-L STICK DOWN", - "L STICK LEFT": "-L STICK LEFT", - "L STICK RIGHT": "-L STICK RIGHT", - "R STICK UP": "-R STICK UP", - "R STICK DOWN": "-R STICK DOWN", - "R STICK LEFT": "-R STICK LEFT", - "R STICK RIGHT": "-R STICK RIGHT", - "Start": "-Start", - "Select": "-Select", - "Fast": "-Fast", - "Slow": "-Slow", - "a": "-a", - "b": "-b", - "c": "-c", - "d": "-d", - "e": "-e", - "f": "-f", - "g": "-g", - "h": "-h", - "i": "-i", - "j": "-j", - "k": "-k", - "l": "-l", - "m": "-m", - "n": "-n", - "o": "-o", - "p": "-p", - "q": "-q", - "r": "-r", - "s": "-s", - "t": "-t", - "u": "-u", - "v": "-v", - "w": "-w", - "x": "-x", - "y": "-y", - "z": "-z", - "enter": "-enter", - "escape": "-escape", - "space": "-space", - "tab": "-tab", - "backspace": "-backspace", - "delete": "-delete", - "arrowup": "-arrowup", - "arrowdown": "-arrowdown", - "arrowleft": "-arrowleft", - "arrowright": "-arrowright", - "f1": "-f1", - "f2": "-f2", - "f3": "-f3", - "f4": "-f4", - "f5": "-f5", - "f6": "-f6", - "f7": "-f7", - "f8": "-f8", - "f9": "-f9", - "f10": "-f10", - "f11": "-f11", - "f12": "-f12", - "shift": "-shift", - "control": "-control", - "alt": "-alt", - "meta": "-meta", - "capslock": "-capslock", - "insert": "-insert", - "home": "-home", - "end": "-end", - "pageup": "-pageup", - "pagedown": "-pagedown", - "!": "-!", - "@": "-@", - "#": "-#", - "$": "-$", - "%": "-%", - "^": "-^", - "&": "-&", - "*": "-*", - "(": "-(", - ")": "-)", - "-": "--", - "_": "-_", - "+": "-+", - "=": "-=", - "[": "-[", - "]": "-]", - "{": "-{", - "}": "-}", - ";": "-;", - ":": "-:", - "'": "-'", - "\"": "-\"", - ",": "-,", - ".": "-.", - "<": "-<", - ">": "->", - "/": "-/", - "?": "-?", - "LEFT_STICK_X": "-LEFT_STICK_X", - "LEFT_STICK_Y": "-LEFT_STICK_Y", - "RIGHT_STICK_X": "-RIGHT_STICK_X", - "RIGHT_STICK_Y": "-RIGHT_STICK_Y", - "LEFT_TRIGGER": "-LEFT_TRIGGER", - "RIGHT_TRIGGER": "-RIGHT_TRIGGER", - "A_BUTTON": "-A_BUTTON", - "B_BUTTON": "-B_BUTTON", - "X_BUTTON": "-X_BUTTON", - "Y_BUTTON": "-Y_BUTTON", - "START_BUTTON": "-START_BUTTON", - "SELECT_BUTTON": "-SELECT_BUTTON", - "L1_BUTTON": "-L1_BUTTON", - "R1_BUTTON": "-R1_BUTTON", - "L2_BUTTON": "-L2_BUTTON", - "R2_BUTTON": "-R2_BUTTON", - "LEFT_THUMB_BUTTON": "-LEFT_THUMB_BUTTON", - "RIGHT_THUMB_BUTTON": "-RIGHT_THUMB_BUTTON", - "DPAD_UP": "-DPAD_UP", - "DPAD_DOWN": "-DPAD_DOWN", - "DPAD_LEFT": "-DPAD_LEFT", - "DPAD_RIGHT": "-DPAD_RIGHT" -} \ No newline at end of file diff --git a/data/localization/retroarch.json b/data/localization/retroarch.json index 4bd1f9b39..4ea0be5eb 100644 --- a/data/localization/retroarch.json +++ b/data/localization/retroarch.json @@ -1,617 +1,617 @@ { - "fceumm region": "-fceumm region", - "fceumm sndquality": "-fceumm sndquality", - "fceumm aspect": "-fceumm aspect", - "fceumm overscan h left": "-fceumm overscan h left", - "fceumm overscan h right": "-fceumm overscan h right", - "fceumm overscan v top": "-fceumm overscan v top", - "fceumm overscan v bottom": "-fceumm overscan v bottom", - "fceumm turbo enable": "-fceumm turbo enable", - "fceumm turbo delay": "-fceumm turbo delay", - "fceumm zapper tolerance": "-fceumm zapper tolerance", - "fceumm mouse sensitivity": "-fceumm mouse sensitivity", - "50%": "-50%", - "60%": "-60%", - "70%": "-70%", - "80%": "-80%", - "90%": "-90%", - "100%": "-100%", - "150%": "-150%", - "200%": "-200%", - "250%": "-250%", - "300%": "-300%", - "350%": "-350%", - "400%": "-400%", - "450%": "-450%", - "500%": "-500%", - "snes9x overclock superfx": "-snes9x overclock superfx", - "snes9x superscope crosshair": "-snes9x superscope crosshair", - "White": "-White", - "White (blend)": "-White (blend)", - "Red": "-Red", - "Red (blend)": "-Red (blend)", - "Orange": "-Orange", - "Orange (blend)": "-Orange (blend)", - "Yellow": "-Yellow", - "Yellow (blend)": "-Yellow (blend)", - "Green": "-Green", - "Green (blend)": "-Green (blend)", - "Cyan": "-Cyan", - "Cyan (blend)": "-Cyan (blend)", - "Sky": "-Sky", - "Sky (blend)": "-Sky (blend)", - "Blue": "-Blue", - "Blue (blend)": "-Blue (blend)", - "Violet": "-Violet", - "Violet (blend)": "-Violet (blend)", - "Pink": "-Pink", - "Pink (blend)": "-Pink (blend)", - "Purple": "-Purple", - "Purple (blend)": "-Purple (blend)", - "Black": "-Black", - "Black (blend)": "-Black (blend)", - "25% Grey": "-25% Grey", - "25% Grey (blend)": "-25% Grey (blend)", - "50% Grey": "-50% Grey", - "50% Grey (blend)": "-50% Grey (blend)", - "75% Grey": "-75% Grey", - "75% Grey (blend)": "-75% Grey (blend)", - "snes9x superscope color": "-snes9x superscope color", - "snes9x justifier1 crosshair": "-snes9x justifier1 crosshair", - "snes9x justifier1 color": "-snes9x justifier1 color", - "snes9x justifier2 crosshair": "-snes9x justifier2 crosshair", - "snes9x justifier2 color": "-snes9x justifier2 color", - "snes9x rifle crosshair": "-snes9x rifle crosshair", - "snes9x rifle color": "-snes9x rifle color", - "1.0x (12.50Mhz)": "-1.0x (12.50Mhz)", - "1.1x (13.75Mhz)": "-1.1x (13.75Mhz)", - "1.2x (15.00Mhz)": "-1.2x (15.00Mhz)", - "1.5x (18.75Mhz)": "-1.5x (18.75Mhz)", - "1.6x (20.00Mhz)": "-1.6x (20.00Mhz)", - "1.8x (22.50Mhz)": "-1.8x (22.50Mhz)", - "2.0x (25.00Mhz)": "-2.0x (25.00Mhz)", - "opera cpu overclock": "-opera cpu overclock", - "0RGB1555": "-0RGB1555", - "RGB565": "-RGB565", - "XRGB8888": "-XRGB8888", - "opera vdlp pixel format": "-opera vdlp pixel format", - "opera nvram version": "-opera nvram version", - "opera active devices": "-opera active devices", - "stella2014 stelladaptor analog sensitivity": "-stella2014 stelladaptor analog sensitivity", - "stella2014 stelladaptor analog center": "-stella2014 stelladaptor analog center", - "handy frameskip threshold": "-handy frameskip threshold", - "320x240": "-320x240", - "640x480": "-640x480", - "960x720": "-960x720", - "1280x960": "-1280x960", - "1440x1080": "-1440x1080", - "1600x1200": "-1600x1200", - "1920x1440": "-1920x1440", - "2240x1680": "-2240x1680", - "2560x1920": "-2560x1920", - "2880x2160": "-2880x2160", - "3200x2400": "-3200x2400", - "3520x2640": "-3520x2640", - "3840x2880": "-3840x2880", - "43screensize": "-43screensize", - "3point": "-3point", - "standard": "-standard", - "BilinearMode": "-BilinearMode", - "MultiSampling": "-MultiSampling", - "FXAA": "-FXAA", - "Software": "-Software", - "FromMem": "-FromMem", - "EnableCopyDepthToRDRAM": "-EnableCopyDepthToRDRAM", - "Stripped": "-Stripped", - "OnePiece": "-OnePiece", - "BackgroundMode": "-BackgroundMode", - "OverscanTop": "-OverscanTop", - "OverscanLeft": "-OverscanLeft", - "OverscanRight": "-OverscanRight", - "OverscanBottom": "-OverscanBottom", - "MaxHiResTxVramLimit": "-MaxHiResTxVramLimit", - "MaxTxCacheSize": "-MaxTxCacheSize", - "Smooth filtering 1": "-Smooth filtering 1", - "Smooth filtering 2": "-Smooth filtering 2", - "Smooth filtering 3": "-Smooth filtering 3", - "Smooth filtering 4": "-Smooth filtering 4", - "Sharp filtering 1": "-Sharp filtering 1", - "Sharp filtering 2": "-Sharp filtering 2", - "txFilterMode": "-txFilterMode", - "As Is": "-As Is", - "X2": "-X2", - "X2SAI": "-X2SAI", - "HQ2X": "-HQ2X", - "HQ2XS": "-HQ2XS", - "LQ2X": "-LQ2X", - "LQ2XS": "-LQ2XS", - "HQ4X": "-HQ4X", - "2xBRZ": "-2xBRZ", - "3xBRZ": "-3xBRZ", - "4xBRZ": "-4xBRZ", - "5xBRZ": "-5xBRZ", - "6xBRZ": "-6xBRZ", - "txEnhancementMode": "-txEnhancementMode", - "Original": "-Original", - "Fullspeed": "-Fullspeed", - "Framerate": "-Framerate", - "virefresh": "-virefresh", - "CountPerOp": "-CountPerOp", - "CountPerOpDenomPot": "-CountPerOpDenomPot", - "deadzone": "-deadzone", - "sensitivity": "-sensitivity", - "C1": "-C1", - "C2": "-C2", - "C3": "-C3", - "C4": "-C4", - "cbutton": "-cbutton", - "none": "-none", - "memory": "-memory", - "rumble": "-rumble", - "transfer": "-transfer", - "pak1": "-pak1", - "pak2": "-pak2", - "pak3": "-pak3", - "pak4": "-pak4", - "Autodetect": "-Autodetect", - "Game Boy": "-Game Boy", - "Super Game Boy": "-Super Game Boy", - "Game Boy Color": "-Game Boy Color", - "Game Boy Advance": "-Game Boy Advance", - "mgba gb model": "-mgba gb model", - "ON": "-ON", - "OFF": "-OFF", - "mgba use bios": "-mgba use bios", - "mgba skip bios": "-mgba skip bios", - "Grayscale": "-Grayscale", - "DMG Green": "-DMG Green", - "GB Pocket": "-GB Pocket", - "GB Light": "-GB Light", - "GBC Brown ↑": "-GBC Brown ↑", - "GBC Red ↑A": "-GBC Red ↑A", - "GBC Dark Brown ↑B": "-GBC Dark Brown ↑B", - "GBC Pale Yellow ↓": "-GBC Pale Yellow ↓", - "GBC Orange ↓A": "-GBC Orange ↓A", - "GBC Yellow ↓B": "-GBC Yellow ↓B", - "GBC Blue ←": "-GBC Blue ←", - "GBC Dark Blue ←A": "-GBC Dark Blue ←A", - "GBC Gray ←B": "-GBC Gray ←B", - "GBC Green →": "-GBC Green →", - "GBC Dark Green →A": "-GBC Dark Green →A", - "GBC Reverse →B": "-GBC Reverse →B", - "SGB 1-A": "-SGB 1-A", - "SGB 1-B": "-SGB 1-B", - "SGB 1-C": "-SGB 1-C", - "SGB 1-D": "-SGB 1-D", - "SGB 1-E": "-SGB 1-E", - "SGB 1-F": "-SGB 1-F", - "SGB 1-G": "-SGB 1-G", - "SGB 1-H": "-SGB 1-H", - "SGB 2-A": "-SGB 2-A", - "SGB 2-B": "-SGB 2-B", - "SGB 2-C": "-SGB 2-C", - "SGB 2-D": "-SGB 2-D", - "SGB 2-E": "-SGB 2-E", - "SGB 2-F": "-SGB 2-F", - "SGB 2-G": "-SGB 2-G", - "SGB 2-H": "-SGB 2-H", - "SGB 3-A": "-SGB 3-A", - "SGB 3-B": "-SGB 3-B", - "SGB 3-C": "-SGB 3-C", - "SGB 3-D": "-SGB 3-D", - "SGB 3-E": "-SGB 3-E", - "SGB 3-F": "-SGB 3-F", - "SGB 3-G": "-SGB 3-G", - "SGB 3-H": "-SGB 3-H", - "SGB 4-A": "-SGB 4-A", - "SGB 4-B": "-SGB 4-B", - "SGB 4-C": "-SGB 4-C", - "SGB 4-D": "-SGB 4-D", - "SGB 4-E": "-SGB 4-E", - "SGB 4-F": "-SGB 4-F", - "SGB 4-G": "-SGB 4-G", - "SGB 4-H": "-SGB 4-H", - "mgba gb colors": "-mgba gb colors", - "mgba sgb borders": "-mgba sgb borders", - "mgba color correction": "-mgba color correction", - "mgba solar sensor level": "-mgba solar sensor level", - "mgba force gbp": "-mgba force gbp", - "Remove Known": "-Remove Known", - "Detect and Remove": "-Detect and Remove", - "Don't Remove": "-Don't Remove", - "mgba idle optimization": "-mgba idle optimization", - "mgba frameskip threshold": "-mgba frameskip threshold", - "mgba frameskip interval": "-mgba frameskip interval", - "GB - DMG": "-GB - DMG", - "GB - Pocket": "-GB - Pocket", - "GB - Light": "-GB - Light", - "GBC - Blue": "-GBC - Blue", - "GBC - Brown": "-GBC - Brown", - "GBC - Dark Blue": "-GBC - Dark Blue", - "GBC - Dark Brown": "-GBC - Dark Brown", - "GBC - Dark Green": "-GBC - Dark Green", - "GBC - Grayscale": "-GBC - Grayscale", - "GBC - Green": "-GBC - Green", - "GBC - Inverted": "-GBC - Inverted", - "GBC - Orange": "-GBC - Orange", - "GBC - Pastel Mix": "-GBC - Pastel Mix", - "GBC - Red": "-GBC - Red", - "GBC - Yellow": "-GBC - Yellow", - "SGB - 1A": "-SGB - 1A", - "SGB - 1B": "-SGB - 1B", - "SGB - 1C": "-SGB - 1C", - "SGB - 1D": "-SGB - 1D", - "SGB - 1E": "-SGB - 1E", - "SGB - 1F": "-SGB - 1F", - "SGB - 1G": "-SGB - 1G", - "SGB - 1H": "-SGB - 1H", - "SGB - 2A": "-SGB - 2A", - "SGB - 2B": "-SGB - 2B", - "SGB - 2C": "-SGB - 2C", - "SGB - 2D": "-SGB - 2D", - "SGB - 2E": "-SGB - 2E", - "SGB - 2F": "-SGB - 2F", - "SGB - 2G": "-SGB - 2G", - "SGB - 2H": "-SGB - 2H", - "SGB - 3A": "-SGB - 3A", - "SGB - 3B": "-SGB - 3B", - "SGB - 3C": "-SGB - 3C", - "SGB - 3D": "-SGB - 3D", - "SGB - 3E": "-SGB - 3E", - "SGB - 3F": "-SGB - 3F", - "SGB - 3G": "-SGB - 3G", - "SGB - 3H": "-SGB - 3H", - "SGB - 4A": "-SGB - 4A", - "SGB - 4B": "-SGB - 4B", - "SGB - 4C": "-SGB - 4C", - "SGB - 4D": "-SGB - 4D", - "SGB - 4E": "-SGB - 4E", - "SGB - 4F": "-SGB - 4F", - "SGB - 4G": "-SGB - 4G", - "SGB - 4H": "-SGB - 4H", - "Special 1": "-Special 1", - "Special 2": "-Special 2", - "Special 3": "-Special 3", - "Special 4 (TI-83 Legacy)": "-Special 4 (TI-83 Legacy)", - "TWB64 - Pack 1": "-TWB64 - Pack 1", - "TWB64 - Pack 2": "-TWB64 - Pack 2", - "PixelShift - Pack 1": "-PixelShift - Pack 1", - "gambatte gb internal palette": "-gambatte gb internal palette", - "TWB64 001 - Aqours Blue": "-TWB64 001 - Aqours Blue", - "TWB64 002 - Anime Expo Ver.": "-TWB64 002 - Anime Expo Ver.", - "TWB64 003 - SpongeBob Yellow": "-TWB64 003 - SpongeBob Yellow", - "TWB64 004 - Patrick Star Pink": "-TWB64 004 - Patrick Star Pink", - "TWB64 005 - Neon Red": "-TWB64 005 - Neon Red", - "TWB64 006 - Neon Blue": "-TWB64 006 - Neon Blue", - "TWB64 007 - Neon Yellow": "-TWB64 007 - Neon Yellow", - "TWB64 008 - Neon Green": "-TWB64 008 - Neon Green", - "TWB64 009 - Neon Pink": "-TWB64 009 - Neon Pink", - "TWB64 010 - Mario Red": "-TWB64 010 - Mario Red", - "TWB64 011 - Nick Orange": "-TWB64 011 - Nick Orange", - "TWB64 012 - Virtual Boy Ver.": "-TWB64 012 - Virtual Boy Ver.", - "TWB64 013 - Golden Wild": "-TWB64 013 - Golden Wild", - "TWB64 014 - Builder Yellow": "-TWB64 014 - Builder Yellow", - "TWB64 015 - Classic Blurple": "-TWB64 015 - Classic Blurple", - "TWB64 016 - 765 Production Ver.": "-TWB64 016 - 765 Production Ver.", - "TWB64 017 - Superball Ivory": "-TWB64 017 - Superball Ivory", - "TWB64 018 - Crunchyroll Orange": "-TWB64 018 - Crunchyroll Orange", - "TWB64 019 - Muse Pink": "-TWB64 019 - Muse Pink", - "TWB64 020 - Nijigasaki Yellow": "-TWB64 020 - Nijigasaki Yellow", - "TWB64 021 - Gamate Ver.": "-TWB64 021 - Gamate Ver.", - "TWB64 022 - Greenscale Ver.": "-TWB64 022 - Greenscale Ver.", - "TWB64 023 - Odyssey Gold": "-TWB64 023 - Odyssey Gold", - "TWB64 024 - Super Saiyan God": "-TWB64 024 - Super Saiyan God", - "TWB64 025 - Super Saiyan Blue": "-TWB64 025 - Super Saiyan Blue", - "TWB64 026 - Bizarre Pink": "-TWB64 026 - Bizarre Pink", - "TWB64 027 - Nintendo Switch Lite Ver.": "-TWB64 027 - Nintendo Switch Lite Ver.", - "TWB64 028 - Game.com Ver.": "-TWB64 028 - Game.com Ver.", - "TWB64 029 - Sanrio Pink": "-TWB64 029 - Sanrio Pink", - "TWB64 030 - BANDAI NAMCO Ver.": "-TWB64 030 - BANDAI NAMCO Ver.", - "TWB64 031 - Cosmo Green": "-TWB64 031 - Cosmo Green", - "TWB64 032 - Wanda Pink": "-TWB64 032 - Wanda Pink", - "TWB64 033 - Link's Awakening DX Ver.": "-TWB64 033 - Link's Awakening DX Ver.", - "TWB64 034 - Travel Wood": "-TWB64 034 - Travel Wood", - "TWB64 035 - Pokemon Ver.": "-TWB64 035 - Pokemon Ver.", - "TWB64 036 - Game Grump Orange": "-TWB64 036 - Game Grump Orange", - "TWB64 037 - Scooby-Doo Mystery Ver.": "-TWB64 037 - Scooby-Doo Mystery Ver.", - "TWB64 038 - Pokemon mini Ver.": "-TWB64 038 - Pokemon mini Ver.", - "TWB64 039 - Supervision Ver.": "-TWB64 039 - Supervision Ver.", - "TWB64 040 - DMG Ver.": "-TWB64 040 - DMG Ver.", - "TWB64 041 - Pocket Ver.": "-TWB64 041 - Pocket Ver.", - "TWB64 042 - Light Ver.": "-TWB64 042 - Light Ver.", - "TWB64 043 - Miraitowa Blue": "-TWB64 043 - Miraitowa Blue", - "TWB64 044 - Someity Pink": "-TWB64 044 - Someity Pink", - "TWB64 045 - Pikachu Yellow": "-TWB64 045 - Pikachu Yellow", - "TWB64 046 - Eevee Brown": "-TWB64 046 - Eevee Brown", - "TWB64 047 - Microvision Ver.": "-TWB64 047 - Microvision Ver.", - "TWB64 048 - TI-83 Ver.": "-TWB64 048 - TI-83 Ver.", - "TWB64 049 - Aegis Cherry": "-TWB64 049 - Aegis Cherry", - "TWB64 050 - Labo Fawn": "-TWB64 050 - Labo Fawn", - "TWB64 051 - MILLION LIVE GOLD!": "-TWB64 051 - MILLION LIVE GOLD!", - "TWB64 052 - Tokyo Midtown Ver.": "-TWB64 052 - Tokyo Midtown Ver.", - "TWB64 053 - VMU Ver.": "-TWB64 053 - VMU Ver.", - "TWB64 054 - Game Master Ver.": "-TWB64 054 - Game Master Ver.", - "TWB64 055 - Android Green": "-TWB64 055 - Android Green", - "TWB64 056 - Ticketmaster Azure": "-TWB64 056 - Ticketmaster Azure", - "TWB64 057 - Google Red": "-TWB64 057 - Google Red", - "TWB64 058 - Google Blue": "-TWB64 058 - Google Blue", - "TWB64 059 - Google Yellow": "-TWB64 059 - Google Yellow", - "TWB64 060 - Google Green": "-TWB64 060 - Google Green", - "TWB64 061 - WonderSwan Ver.": "-TWB64 061 - WonderSwan Ver.", - "TWB64 062 - Neo Geo Pocket Ver.": "-TWB64 062 - Neo Geo Pocket Ver.", - "TWB64 063 - Dew Green": "-TWB64 063 - Dew Green", - "TWB64 064 - Coca-Cola Red": "-TWB64 064 - Coca-Cola Red", - "TWB64 065 - GameKing Ver.": "-TWB64 065 - GameKing Ver.", - "TWB64 066 - Do The Dew Ver.": "-TWB64 066 - Do The Dew Ver.", - "TWB64 067 - Digivice Ver.": "-TWB64 067 - Digivice Ver.", - "TWB64 068 - Bikini Bottom Ver.": "-TWB64 068 - Bikini Bottom Ver.", - "TWB64 069 - Blossom Pink": "-TWB64 069 - Blossom Pink", - "TWB64 070 - Bubbles Blue": "-TWB64 070 - Bubbles Blue", - "TWB64 071 - Buttercup Green": "-TWB64 071 - Buttercup Green", - "TWB64 072 - NASCAR Ver.": "-TWB64 072 - NASCAR Ver.", - "TWB64 073 - Lemon-Lime Green": "-TWB64 073 - Lemon-Lime Green", - "TWB64 074 - Mega Man V Ver.": "-TWB64 074 - Mega Man V Ver.", - "TWB64 075 - Tamagotchi Ver.": "-TWB64 075 - Tamagotchi Ver.", - "TWB64 076 - Phantom Red": "-TWB64 076 - Phantom Red", - "TWB64 077 - Halloween Ver.": "-TWB64 077 - Halloween Ver.", - "TWB64 078 - Christmas Ver.": "-TWB64 078 - Christmas Ver.", - "TWB64 079 - Cardcaptor Pink": "-TWB64 079 - Cardcaptor Pink", - "TWB64 080 - Pretty Guardian Gold": "-TWB64 080 - Pretty Guardian Gold", - "TWB64 081 - Camouflage Ver.": "-TWB64 081 - Camouflage Ver.", - "TWB64 082 - Legendary Super Saiyan": "-TWB64 082 - Legendary Super Saiyan", - "TWB64 083 - Super Saiyan Rose": "-TWB64 083 - Super Saiyan Rose", - "TWB64 084 - Super Saiyan": "-TWB64 084 - Super Saiyan", - "TWB64 085 - Perfected Ultra Instinct": "-TWB64 085 - Perfected Ultra Instinct", - "TWB64 086 - Saint Snow Red": "-TWB64 086 - Saint Snow Red", - "TWB64 087 - Yellow Banana": "-TWB64 087 - Yellow Banana", - "TWB64 088 - Green Banana": "-TWB64 088 - Green Banana", - "TWB64 089 - Super Saiyan 3": "-TWB64 089 - Super Saiyan 3", - "TWB64 090 - Super Saiyan Blue Evolved": "-TWB64 090 - Super Saiyan Blue Evolved", - "TWB64 091 - Pocket Tales Ver.": "-TWB64 091 - Pocket Tales Ver.", - "TWB64 092 - Investigation Yellow": "-TWB64 092 - Investigation Yellow", - "TWB64 093 - S.E.E.S. Blue": "-TWB64 093 - S.E.E.S. Blue", - "TWB64 094 - Game Awards Cyan": "-TWB64 094 - Game Awards Cyan", - "TWB64 095 - Hokage Orange": "-TWB64 095 - Hokage Orange", - "TWB64 096 - Straw Hat Red": "-TWB64 096 - Straw Hat Red", - "TWB64 097 - Sword Art Cyan": "-TWB64 097 - Sword Art Cyan", - "TWB64 098 - Deku Alpha Emerald": "-TWB64 098 - Deku Alpha Emerald", - "TWB64 099 - Blue Stripes Ver.": "-TWB64 099 - Blue Stripes Ver.", - "TWB64 100 - Stone Orange": "-TWB64 100 - Stone Orange", - "gambatte gb palette twb64 1": "-gambatte gb palette twb64 1", - "TWB64 101 - 765PRO Pink": "-TWB64 101 - 765PRO Pink", - "TWB64 102 - CINDERELLA Blue": "-TWB64 102 - CINDERELLA Blue", - "TWB64 103 - MILLION Yellow!": "-TWB64 103 - MILLION Yellow!", - "TWB64 104 - SideM Green": "-TWB64 104 - SideM Green", - "TWB64 105 - SHINY Sky Blue": "-TWB64 105 - SHINY Sky Blue", - "TWB64 106 - Angry Volcano Ver.": "-TWB64 106 - Angry Volcano Ver.", - "TWB64 107 - Yo-kai Pink": "-TWB64 107 - Yo-kai Pink", - "TWB64 108 - Yo-kai Green": "-TWB64 108 - Yo-kai Green", - "TWB64 109 - Yo-kai Blue": "-TWB64 109 - Yo-kai Blue", - "TWB64 110 - Yo-kai Purple": "-TWB64 110 - Yo-kai Purple", - "TWB64 111 - Aquatic Iro": "-TWB64 111 - Aquatic Iro", - "TWB64 112 - Tea Midori": "-TWB64 112 - Tea Midori", - "TWB64 113 - Sakura Pink": "-TWB64 113 - Sakura Pink", - "TWB64 114 - Wisteria Murasaki": "-TWB64 114 - Wisteria Murasaki", - "TWB64 115 - Oni Aka": "-TWB64 115 - Oni Aka", - "TWB64 116 - Golden Kiiro": "-TWB64 116 - Golden Kiiro", - "TWB64 117 - Silver Shiro": "-TWB64 117 - Silver Shiro", - "TWB64 118 - Fruity Orange": "-TWB64 118 - Fruity Orange", - "TWB64 119 - AKB48 Pink": "-TWB64 119 - AKB48 Pink", - "TWB64 120 - Miku Blue": "-TWB64 120 - Miku Blue", - "TWB64 121 - Fairy Tail Red": "-TWB64 121 - Fairy Tail Red", - "TWB64 122 - Survey Corps Brown": "-TWB64 122 - Survey Corps Brown", - "TWB64 123 - Island Green": "-TWB64 123 - Island Green", - "TWB64 124 - Mania Plus Green": "-TWB64 124 - Mania Plus Green", - "TWB64 125 - Ninja Turtle Green": "-TWB64 125 - Ninja Turtle Green", - "TWB64 126 - Slime Blue": "-TWB64 126 - Slime Blue", - "TWB64 127 - Lime Midori": "-TWB64 127 - Lime Midori", - "TWB64 128 - Ghostly Aoi": "-TWB64 128 - Ghostly Aoi", - "TWB64 129 - Retro Bogeda": "-TWB64 129 - Retro Bogeda", - "TWB64 130 - Royal Blue": "-TWB64 130 - Royal Blue", - "TWB64 131 - Neon Purple": "-TWB64 131 - Neon Purple", - "TWB64 132 - Neon Orange": "-TWB64 132 - Neon Orange", - "TWB64 133 - Moonlight Vision": "-TWB64 133 - Moonlight Vision", - "TWB64 134 - Tokyo Red": "-TWB64 134 - Tokyo Red", - "TWB64 135 - Paris Gold": "-TWB64 135 - Paris Gold", - "TWB64 136 - Beijing Blue": "-TWB64 136 - Beijing Blue", - "TWB64 137 - Pac-Man Yellow": "-TWB64 137 - Pac-Man Yellow", - "TWB64 138 - Irish Green": "-TWB64 138 - Irish Green", - "TWB64 139 - Kakarot Orange": "-TWB64 139 - Kakarot Orange", - "TWB64 140 - Dragon Ball Orange": "-TWB64 140 - Dragon Ball Orange", - "TWB64 141 - Christmas Gold": "-TWB64 141 - Christmas Gold", - "TWB64 142 - Pepsi-Cola Blue": "-TWB64 142 - Pepsi-Cola Blue", - "TWB64 143 - Bubblun Green": "-TWB64 143 - Bubblun Green", - "TWB64 144 - Bobblun Blue": "-TWB64 144 - Bobblun Blue", - "TWB64 145 - Baja Blast Storm": "-TWB64 145 - Baja Blast Storm", - "TWB64 146 - Olympic Gold": "-TWB64 146 - Olympic Gold", - "TWB64 147 - Value Orange": "-TWB64 147 - Value Orange", - "TWB64 148 - Liella Purple!": "-TWB64 148 - Liella Purple!", - "TWB64 149 - Olympic Silver": "-TWB64 149 - Olympic Silver", - "TWB64 150 - Olympic Bronze": "-TWB64 150 - Olympic Bronze", - "TWB64 151 - ANA Sky Blue": "-TWB64 151 - ANA Sky Blue", - "TWB64 152 - Nijigasaki Orange": "-TWB64 152 - Nijigasaki Orange", - "TWB64 153 - HoloBlue": "-TWB64 153 - HoloBlue", - "TWB64 154 - Wrestling Red": "-TWB64 154 - Wrestling Red", - "TWB64 155 - Yoshi Egg Green": "-TWB64 155 - Yoshi Egg Green", - "TWB64 156 - Pokedex Red": "-TWB64 156 - Pokedex Red", - "TWB64 157 - Disney Dream Blue": "-TWB64 157 - Disney Dream Blue", - "TWB64 158 - Xbox Green": "-TWB64 158 - Xbox Green", - "TWB64 159 - Sonic Mega Blue": "-TWB64 159 - Sonic Mega Blue", - "TWB64 160 - G4 Orange": "-TWB64 160 - G4 Orange", - "TWB64 161 - Scarlett Green": "-TWB64 161 - Scarlett Green", - "TWB64 162 - Glitchy Blue": "-TWB64 162 - Glitchy Blue", - "TWB64 163 - Classic LCD": "-TWB64 163 - Classic LCD", - "TWB64 164 - 3DS Virtual Console Ver.": "-TWB64 164 - 3DS Virtual Console Ver.", - "TWB64 165 - PocketStation Ver.": "-TWB64 165 - PocketStation Ver.", - "TWB64 166 - Game and Gold": "-TWB64 166 - Game and Gold", - "TWB64 167 - Smurfy Blue": "-TWB64 167 - Smurfy Blue", - "TWB64 168 - Swampy Ogre Green": "-TWB64 168 - Swampy Ogre Green", - "TWB64 169 - Sailor Spinach Green": "-TWB64 169 - Sailor Spinach Green", - "TWB64 170 - Shenron Green": "-TWB64 170 - Shenron Green", - "TWB64 171 - Berserk Blood": "-TWB64 171 - Berserk Blood", - "TWB64 172 - Super Star Pink": "-TWB64 172 - Super Star Pink", - "TWB64 173 - Gamebuino Classic Ver.": "-TWB64 173 - Gamebuino Classic Ver.", - "TWB64 174 - Barbie Pink": "-TWB64 174 - Barbie Pink", - "TWB64 175 - Star Command Green": "-TWB64 175 - Star Command Green", - "TWB64 176 - Nokia 3310 Ver.": "-TWB64 176 - Nokia 3310 Ver.", - "TWB64 177 - Clover Green": "-TWB64 177 - Clover Green", - "TWB64 178 - Crash Orange": "-TWB64 178 - Crash Orange", - "TWB64 179 - Famicom Disk Yellow": "-TWB64 179 - Famicom Disk Yellow", - "TWB64 180 - Team Rocket Red": "-TWB64 180 - Team Rocket Red", - "TWB64 181 - SEIKO Timer Yellow": "-TWB64 181 - SEIKO Timer Yellow", - "TWB64 182 - PINK109": "-TWB64 182 - PINK109", - "TWB64 183 - Doraemon Blue": "-TWB64 183 - Doraemon Blue", - "TWB64 184 - Fury Blue": "-TWB64 184 - Fury Blue", - "TWB64 185 - Rockstar Orange": "-TWB64 185 - Rockstar Orange", - "TWB64 186 - Puyo Puyo Green": "-TWB64 186 - Puyo Puyo Green", - "TWB64 187 - Susan G. Pink": "-TWB64 187 - Susan G. Pink", - "TWB64 188 - Pizza Hut Red": "-TWB64 188 - Pizza Hut Red", - "TWB64 189 - Plumbob Green": "-TWB64 189 - Plumbob Green", - "TWB64 190 - Grand Ivory": "-TWB64 190 - Grand Ivory", - "TWB64 191 - Demon's Gold": "-TWB64 191 - Demon's Gold", - "TWB64 192 - SEGA Tokyo Blue": "-TWB64 192 - SEGA Tokyo Blue", - "TWB64 193 - Champion Blue": "-TWB64 193 - Champion Blue", - "TWB64 194 - DK Barrel Brown": "-TWB64 194 - DK Barrel Brown", - "TWB64 195 - Evangelion Green": "-TWB64 195 - Evangelion Green", - "TWB64 196 - Equestrian Purple": "-TWB64 196 - Equestrian Purple", - "TWB64 197 - Autobot Red": "-TWB64 197 - Autobot Red", - "TWB64 198 - Niconico Sea Green": "-TWB64 198 - Niconico Sea Green", - "TWB64 199 - Duracell Copper": "-TWB64 199 - Duracell Copper", - "TWB64 200 - TOKYO SKYTREE CLOUDY BLUE": "-TWB64 200 - TOKYO SKYTREE CLOUDY BLUE", - "gambatte gb palette twb64 2": "-gambatte gb palette twb64 2", - "PixelShift 01 - Arctic Green": "-PixelShift 01 - Arctic Green", - "PixelShift 02 - Arduboy": "-PixelShift 02 - Arduboy", - "PixelShift 03 - BGB 0.3 Emulator": "-PixelShift 03 - BGB 0.3 Emulator", - "PixelShift 04 - Camouflage": "-PixelShift 04 - Camouflage", - "PixelShift 05 - Chocolate Bar": "-PixelShift 05 - Chocolate Bar", - "PixelShift 06 - CMYK": "-PixelShift 06 - CMYK", - "PixelShift 07 - Cotton Candy": "-PixelShift 07 - Cotton Candy", - "PixelShift 08 - Easy Greens": "-PixelShift 08 - Easy Greens", - "PixelShift 09 - Gamate": "-PixelShift 09 - Gamate", - "PixelShift 10 - Game Boy Light": "-PixelShift 10 - Game Boy Light", - "PixelShift 11 - Game Boy Pocket": "-PixelShift 11 - Game Boy Pocket", - "PixelShift 12 - Game Boy Pocket Alt": "-PixelShift 12 - Game Boy Pocket Alt", - "PixelShift 13 - Game Pocket Computer": "-PixelShift 13 - Game Pocket Computer", - "PixelShift 14 - Game & Watch Ball": "-PixelShift 14 - Game & Watch Ball", - "PixelShift 15 - GB Backlight Blue": "-PixelShift 15 - GB Backlight Blue", - "PixelShift 16 - GB Backlight Faded": "-PixelShift 16 - GB Backlight Faded", - "PixelShift 17 - GB Backlight Orange": "-PixelShift 17 - GB Backlight Orange", - "PixelShift 18 - GB Backlight White ": "-PixelShift 18 - GB Backlight White ", - "PixelShift 19 - GB Backlight Yellow Dark": "-PixelShift 19 - GB Backlight Yellow Dark", - "PixelShift 20 - GB Bootleg": "-PixelShift 20 - GB Bootleg", - "PixelShift 21 - GB Hunter": "-PixelShift 21 - GB Hunter", - "PixelShift 22 - GB Kiosk": "-PixelShift 22 - GB Kiosk", - "PixelShift 23 - GB Kiosk 2": "-PixelShift 23 - GB Kiosk 2", - "PixelShift 24 - GB New": "-PixelShift 24 - GB New", - "PixelShift 25 - GB Nuked": "-PixelShift 25 - GB Nuked", - "PixelShift 26 - GB Old": "-PixelShift 26 - GB Old", - "PixelShift 27 - GBP Bivert": "-PixelShift 27 - GBP Bivert", - "PixelShift 28 - GB Washed Yellow Backlight": "-PixelShift 28 - GB Washed Yellow Backlight", - "PixelShift 29 - Ghost": "-PixelShift 29 - Ghost", - "PixelShift 30 - Glow In The Dark": "-PixelShift 30 - Glow In The Dark", - "PixelShift 31 - Gold Bar": "-PixelShift 31 - Gold Bar", - "PixelShift 32 - Grapefruit": "-PixelShift 32 - Grapefruit", - "PixelShift 33 - Gray Green Mix": "-PixelShift 33 - Gray Green Mix", - "PixelShift 34 - Missingno": "-PixelShift 34 - Missingno", - "PixelShift 35 - MS-Dos": "-PixelShift 35 - MS-Dos", - "PixelShift 36 - Newspaper": "-PixelShift 36 - Newspaper", - "PixelShift 37 - Pip-Boy": "-PixelShift 37 - Pip-Boy", - "PixelShift 38 - Pocket Girl": "-PixelShift 38 - Pocket Girl", - "PixelShift 39 - Silhouette": "-PixelShift 39 - Silhouette", - "PixelShift 40 - Sunburst": "-PixelShift 40 - Sunburst", - "PixelShift 41 - Technicolor": "-PixelShift 41 - Technicolor", - "PixelShift 42 - Tron": "-PixelShift 42 - Tron", - "PixelShift 43 - Vaporwave": "-PixelShift 43 - Vaporwave", - "PixelShift 44 - Virtual Boy": "-PixelShift 44 - Virtual Boy", - "PixelShift 45 - Wish": "-PixelShift 45 - Wish", - "gambatte gb palette pixelshift 1": "-gambatte gb palette pixelshift 1", - "gambatte dark filter level": "-gambatte dark filter level", - "GB": "-GB", - "GBC": "-GBC", - "GBA": "-GBA", - "gambatte gb hwmode": "-gambatte gb hwmode", - "gambatte turbo period": "-gambatte turbo period", - "gambatte rumble level": "-gambatte rumble level", - "pcsx rearmed psxclock": "-pcsx rearmed psxclock", - "pcsx rearmed frameskip threshold": "-pcsx rearmed frameskip threshold", - "pcsx rearmed frameskip interval": "-pcsx rearmed frameskip interval", - "pcsx rearmed input sensitivity": "-pcsx rearmed input sensitivity", - "pcsx rearmed gunconadjustx": "-pcsx rearmed gunconadjustx", - "pcsx rearmed gunconadjusty": "-pcsx rearmed gunconadjusty", - "pcsx rearmed gunconadjustratiox": "-pcsx rearmed gunconadjustratiox", - "pcsx rearmed gunconadjustratioy": "-pcsx rearmed gunconadjustratioy", - "Japan NTSC": "-Japan NTSC", - "Japan PAL": "-Japan PAL", - "US": "-US", - "Europe": "-Europe", - "picodrive region": "-picodrive region", - "Game Gear": "-Game Gear", - "Master System": "-Master System", - "SG-1000": "-SG-1000", - "SC-3000": "-SC-3000", - "picodrive smstype": "-picodrive smstype", - "Sega": "-Sega", - "Codemasters": "-Codemasters", - "Korea": "-Korea", - "Korea MSX": "-Korea MSX", - "Korea X-in-1": "-Korea X-in-1", - "Korea 4-Pak": "-Korea 4-Pak", - "Korea Janggun": "-Korea Janggun", - "Korea Nemesis": "-Korea Nemesis", - "Taiwan 8K RAM": "-Taiwan 8K RAM", - "picodrive smsmapper": "-picodrive smsmapper", - "PAR": "-PAR", - "CRT": "-CRT", - "picodrive aspect": "-picodrive aspect", - "native": "-native", - "picodrive sound rate": "-picodrive sound rate", - "picodrive lowpass range": "-picodrive lowpass range", - "picodrive frameskip threshold": "-picodrive frameskip threshold", - "genesis plus gx frameskip threshold": "-genesis plus gx frameskip threshold", - "genesis plus gx lowpass range": "-genesis plus gx lowpass range", - "genesis plus gx psg preamp": "-genesis plus gx psg preamp", - "genesis plus gx fm preamp": "-genesis plus gx fm preamp", - "genesis plus gx cdda volume": "-genesis plus gx cdda volume", - "genesis plus gx pcm volume": "-genesis plus gx pcm volume", - "genesis plus gx audio eq low": "-genesis plus gx audio eq low", - "genesis plus gx audio eq mid": "-genesis plus gx audio eq mid", - "genesis plus gx audio eq high": "-genesis plus gx audio eq high", - "genesis plus gx enhanced vscroll limit": "-genesis plus gx enhanced vscroll limit", - "genesis plus gx psg channel 0 volume": "-genesis plus gx psg channel 0 volume", - "genesis plus gx psg channel 1 volume": "-genesis plus gx psg channel 1 volume", - "genesis plus gx psg channel 2 volume": "-genesis plus gx psg channel 2 volume", - "genesis plus gx psg channel 3 volume": "-genesis plus gx psg channel 3 volume", - "genesis plus gx md channel 0 volume": "-genesis plus gx md channel 0 volume", - "genesis plus gx md channel 1 volume": "-genesis plus gx md channel 1 volume", - "genesis plus gx md channel 2 volume": "-genesis plus gx md channel 2 volume", - "genesis plus gx md channel 3 volume": "-genesis plus gx md channel 3 volume", - "genesis plus gx md channel 4 volume": "-genesis plus gx md channel 4 volume", - "genesis plus gx md channel 5 volume": "-genesis plus gx md channel 5 volume", - "genesis plus gx sms fm channel 0 volume": "-genesis plus gx sms fm channel 0 volume", - "genesis plus gx sms fm channel 1 volume": "-genesis plus gx sms fm channel 1 volume", - "genesis plus gx sms fm channel 2 volume": "-genesis plus gx sms fm channel 2 volume", - "genesis plus gx sms fm channel 3 volume": "-genesis plus gx sms fm channel 3 volume", - "genesis plus gx sms fm channel 4 volume": "-genesis plus gx sms fm channel 4 volume", - "genesis plus gx sms fm channel 5 volume": "-genesis plus gx sms fm channel 5 volume", - "genesis plus gx sms fm channel 6 volume": "-genesis plus gx sms fm channel 6 volume", - "genesis plus gx sms fm channel 7 volume": "-genesis plus gx sms fm channel 7 volume", - "genesis plus gx sms fm channel 8 volume": "-genesis plus gx sms fm channel 8 volume", - "anaglyph": "-anaglyph", - "cyberscope": "-cyberscope", - "side-by-side": "-side-by-side", - "vli": "-vli", - "hli": "-hli", - "vb 3dmode": "-vb 3dmode", - "black & red": "-black & red", - "black & white": "-black & white", - "black & blue": "-black & blue", - "black & cyan": "-black & cyan", - "black & electric cyan": "-black & electric cyan", - "black & green": "-black & green", - "black & magenta": "-black & magenta", - "black & yellow": "-black & yellow", - "vb color mode": "-vb color mode", - "accurate": "-accurate", - "fast": "-fast", - "vb cpu emulation": "-vb cpu emulation" -} \ No newline at end of file + "fceumm region": "fceumm region", + "fceumm sndquality": "fceumm sndquality", + "fceumm aspect": "fceumm aspect", + "fceumm overscan h left": "fceumm overscan h left", + "fceumm overscan h right": "fceumm overscan h right", + "fceumm overscan v top": "fceumm overscan v top", + "fceumm overscan v bottom": "fceumm overscan v bottom", + "fceumm turbo enable": "fceumm turbo enable", + "fceumm turbo delay": "fceumm turbo delay", + "fceumm zapper tolerance": "fceumm zapper tolerance", + "fceumm mouse sensitivity": "fceumm mouse sensitivity", + "50%": "50%", + "60%": "60%", + "70%": "70%", + "80%": "80%", + "90%": "90%", + "100%": "100%", + "150%": "150%", + "200%": "200%", + "250%": "250%", + "300%": "300%", + "350%": "350%", + "400%": "400%", + "450%": "450%", + "500%": "500%", + "snes9x overclock superfx": "snes9x overclock superfx", + "snes9x superscope crosshair": "snes9x superscope crosshair", + "White": "White", + "White (blend)": "White (blend)", + "Red": "Red", + "Red (blend)": "Red (blend)", + "Orange": "Orange", + "Orange (blend)": "Orange (blend)", + "Yellow": "Yellow", + "Yellow (blend)": "Yellow (blend)", + "Green": "Green", + "Green (blend)": "Green (blend)", + "Cyan": "Cyan", + "Cyan (blend)": "Cyan (blend)", + "Sky": "Sky", + "Sky (blend)": "Sky (blend)", + "Blue": "Blue", + "Blue (blend)": "Blue (blend)", + "Violet": "Violet", + "Violet (blend)": "Violet (blend)", + "Pink": "Pink", + "Pink (blend)": "Pink (blend)", + "Purple": "Purple", + "Purple (blend)": "Purple (blend)", + "Black": "Black", + "Black (blend)": "Black (blend)", + "25% Grey": "25% Grey", + "25% Grey (blend)": "25% Grey (blend)", + "50% Grey": "50% Grey", + "50% Grey (blend)": "50% Grey (blend)", + "75% Grey": "75% Grey", + "75% Grey (blend)": "75% Grey (blend)", + "snes9x superscope color": "snes9x superscope color", + "snes9x justifier1 crosshair": "snes9x justifier1 crosshair", + "snes9x justifier1 color": "snes9x justifier1 color", + "snes9x justifier2 crosshair": "snes9x justifier2 crosshair", + "snes9x justifier2 color": "snes9x justifier2 color", + "snes9x rifle crosshair": "snes9x rifle crosshair", + "snes9x rifle color": "snes9x rifle color", + "1.0x (12.50Mhz)": "1.0x (12.50Mhz)", + "1.1x (13.75Mhz)": "1.1x (13.75Mhz)", + "1.2x (15.00Mhz)": "1.2x (15.00Mhz)", + "1.5x (18.75Mhz)": "1.5x (18.75Mhz)", + "1.6x (20.00Mhz)": "1.6x (20.00Mhz)", + "1.8x (22.50Mhz)": "1.8x (22.50Mhz)", + "2.0x (25.00Mhz)": "2.0x (25.00Mhz)", + "opera cpu overclock": "opera cpu overclock", + "0RGB1555": "0RGB1555", + "RGB565": "RGB565", + "XRGB8888": "XRGB8888", + "opera vdlp pixel format": "opera vdlp pixel format", + "opera nvram version": "opera nvram version", + "opera active devices": "opera active devices", + "stella2014 stelladaptor analog sensitivity": "stella2014 stelladaptor analog sensitivity", + "stella2014 stelladaptor analog center": "stella2014 stelladaptor analog center", + "handy frameskip threshold": "handy frameskip threshold", + "320x240": "320x240", + "640x480": "640x480", + "960x720": "960x720", + "1280x960": "1280x960", + "1440x1080": "1440x1080", + "1600x1200": "1600x1200", + "1920x1440": "1920x1440", + "2240x1680": "2240x1680", + "2560x1920": "2560x1920", + "2880x2160": "2880x2160", + "3200x2400": "3200x2400", + "3520x2640": "3520x2640", + "3840x2880": "3840x2880", + "43screensize": "43screensize", + "3point": "3point", + "standard": "standard", + "BilinearMode": "BilinearMode", + "MultiSampling": "MultiSampling", + "FXAA": "FXAA", + "Software": "Software", + "FromMem": "FromMem", + "EnableCopyDepthToRDRAM": "EnableCopyDepthToRDRAM", + "Stripped": "Stripped", + "OnePiece": "OnePiece", + "BackgroundMode": "BackgroundMode", + "OverscanTop": "OverscanTop", + "OverscanLeft": "OverscanLeft", + "OverscanRight": "OverscanRight", + "OverscanBottom": "OverscanBottom", + "MaxHiResTxVramLimit": "MaxHiResTxVramLimit", + "MaxTxCacheSize": "MaxTxCacheSize", + "Smooth filtering 1": "Smooth filtering 1", + "Smooth filtering 2": "Smooth filtering 2", + "Smooth filtering 3": "Smooth filtering 3", + "Smooth filtering 4": "Smooth filtering 4", + "Sharp filtering 1": "Sharp filtering 1", + "Sharp filtering 2": "Sharp filtering 2", + "txFilterMode": "txFilterMode", + "As Is": "As Is", + "X2": "X2", + "X2SAI": "X2SAI", + "HQ2X": "HQ2X", + "HQ2XS": "HQ2XS", + "LQ2X": "LQ2X", + "LQ2XS": "LQ2XS", + "HQ4X": "HQ4X", + "2xBRZ": "2xBRZ", + "3xBRZ": "3xBRZ", + "4xBRZ": "4xBRZ", + "5xBRZ": "5xBRZ", + "6xBRZ": "6xBRZ", + "txEnhancementMode": "txEnhancementMode", + "Original": "Original", + "Fullspeed": "Fullspeed", + "Framerate": "Framerate", + "virefresh": "virefresh", + "CountPerOp": "CountPerOp", + "CountPerOpDenomPot": "CountPerOpDenomPot", + "deadzone": "deadzone", + "sensitivity": "sensitivity", + "C1": "C1", + "C2": "C2", + "C3": "C3", + "C4": "C4", + "cbutton": "cbutton", + "none": "none", + "memory": "memory", + "rumble": "rumble", + "transfer": "transfer", + "pak1": "pak1", + "pak2": "pak2", + "pak3": "pak3", + "pak4": "pak4", + "Autodetect": "Autodetect", + "Game Boy": "Game Boy", + "Super Game Boy": "Super Game Boy", + "Game Boy Color": "Game Boy Color", + "Game Boy Advance": "Game Boy Advance", + "mgba gb model": "mgba gb model", + "ON": "ON", + "OFF": "OFF", + "mgba use bios": "mgba use bios", + "mgba skip bios": "mgba skip bios", + "Grayscale": "Grayscale", + "DMG Green": "DMG Green", + "GB Pocket": "GB Pocket", + "GB Light": "GB Light", + "GBC Brown ↑": "GBC Brown ↑", + "GBC Red ↑A": "GBC Red ↑A", + "GBC Dark Brown ↑B": "GBC Dark Brown ↑B", + "GBC Pale Yellow ↓": "GBC Pale Yellow ↓", + "GBC Orange ↓A": "GBC Orange ↓A", + "GBC Yellow ↓B": "GBC Yellow ↓B", + "GBC Blue ←": "GBC Blue ←", + "GBC Dark Blue ←A": "GBC Dark Blue ←A", + "GBC Gray ←B": "GBC Gray ←B", + "GBC Green →": "GBC Green →", + "GBC Dark Green →A": "GBC Dark Green →A", + "GBC Reverse →B": "GBC Reverse →B", + "SGB 1-A": "SGB 1-A", + "SGB 1-B": "SGB 1-B", + "SGB 1-C": "SGB 1-C", + "SGB 1-D": "SGB 1-D", + "SGB 1-E": "SGB 1-E", + "SGB 1-F": "SGB 1-F", + "SGB 1-G": "SGB 1-G", + "SGB 1-H": "SGB 1-H", + "SGB 2-A": "SGB 2-A", + "SGB 2-B": "SGB 2-B", + "SGB 2-C": "SGB 2-C", + "SGB 2-D": "SGB 2-D", + "SGB 2-E": "SGB 2-E", + "SGB 2-F": "SGB 2-F", + "SGB 2-G": "SGB 2-G", + "SGB 2-H": "SGB 2-H", + "SGB 3-A": "SGB 3-A", + "SGB 3-B": "SGB 3-B", + "SGB 3-C": "SGB 3-C", + "SGB 3-D": "SGB 3-D", + "SGB 3-E": "SGB 3-E", + "SGB 3-F": "SGB 3-F", + "SGB 3-G": "SGB 3-G", + "SGB 3-H": "SGB 3-H", + "SGB 4-A": "SGB 4-A", + "SGB 4-B": "SGB 4-B", + "SGB 4-C": "SGB 4-C", + "SGB 4-D": "SGB 4-D", + "SGB 4-E": "SGB 4-E", + "SGB 4-F": "SGB 4-F", + "SGB 4-G": "SGB 4-G", + "SGB 4-H": "SGB 4-H", + "mgba gb colors": "mgba gb colors", + "mgba sgb borders": "mgba sgb borders", + "mgba color correction": "mgba color correction", + "mgba solar sensor level": "mgba solar sensor level", + "mgba force gbp": "mgba force gbp", + "Remove Known": "Remove Known", + "Detect and Remove": "Detect and Remove", + "Don't Remove": "Don't Remove", + "mgba idle optimization": "mgba idle optimization", + "mgba frameskip threshold": "mgba frameskip threshold", + "mgba frameskip interval": "mgba frameskip interval", + "GB - DMG": "GB - DMG", + "GB - Pocket": "GB - Pocket", + "GB - Light": "GB - Light", + "GBC - Blue": "GBC - Blue", + "GBC - Brown": "GBC - Brown", + "GBC - Dark Blue": "GBC - Dark Blue", + "GBC - Dark Brown": "GBC - Dark Brown", + "GBC - Dark Green": "GBC - Dark Green", + "GBC - Grayscale": "GBC - Grayscale", + "GBC - Green": "GBC - Green", + "GBC - Inverted": "GBC - Inverted", + "GBC - Orange": "GBC - Orange", + "GBC - Pastel Mix": "GBC - Pastel Mix", + "GBC - Red": "GBC - Red", + "GBC - Yellow": "GBC - Yellow", + "SGB - 1A": "SGB - 1A", + "SGB - 1B": "SGB - 1B", + "SGB - 1C": "SGB - 1C", + "SGB - 1D": "SGB - 1D", + "SGB - 1E": "SGB - 1E", + "SGB - 1F": "SGB - 1F", + "SGB - 1G": "SGB - 1G", + "SGB - 1H": "SGB - 1H", + "SGB - 2A": "SGB - 2A", + "SGB - 2B": "SGB - 2B", + "SGB - 2C": "SGB - 2C", + "SGB - 2D": "SGB - 2D", + "SGB - 2E": "SGB - 2E", + "SGB - 2F": "SGB - 2F", + "SGB - 2G": "SGB - 2G", + "SGB - 2H": "SGB - 2H", + "SGB - 3A": "SGB - 3A", + "SGB - 3B": "SGB - 3B", + "SGB - 3C": "SGB - 3C", + "SGB - 3D": "SGB - 3D", + "SGB - 3E": "SGB - 3E", + "SGB - 3F": "SGB - 3F", + "SGB - 3G": "SGB - 3G", + "SGB - 3H": "SGB - 3H", + "SGB - 4A": "SGB - 4A", + "SGB - 4B": "SGB - 4B", + "SGB - 4C": "SGB - 4C", + "SGB - 4D": "SGB - 4D", + "SGB - 4E": "SGB - 4E", + "SGB - 4F": "SGB - 4F", + "SGB - 4G": "SGB - 4G", + "SGB - 4H": "SGB - 4H", + "Special 1": "Special 1", + "Special 2": "Special 2", + "Special 3": "Special 3", + "Special 4 (TI-83 Legacy)": "Special 4 (TI-83 Legacy)", + "TWB64 - Pack 1": "TWB64 - Pack 1", + "TWB64 - Pack 2": "TWB64 - Pack 2", + "PixelShift - Pack 1": "PixelShift - Pack 1", + "gambatte gb internal palette": "gambatte gb internal palette", + "TWB64 001 - Aqours Blue": "TWB64 001 - Aqours Blue", + "TWB64 002 - Anime Expo Ver.": "TWB64 002 - Anime Expo Ver.", + "TWB64 003 - SpongeBob Yellow": "TWB64 003 - SpongeBob Yellow", + "TWB64 004 - Patrick Star Pink": "TWB64 004 - Patrick Star Pink", + "TWB64 005 - Neon Red": "TWB64 005 - Neon Red", + "TWB64 006 - Neon Blue": "TWB64 006 - Neon Blue", + "TWB64 007 - Neon Yellow": "TWB64 007 - Neon Yellow", + "TWB64 008 - Neon Green": "TWB64 008 - Neon Green", + "TWB64 009 - Neon Pink": "TWB64 009 - Neon Pink", + "TWB64 010 - Mario Red": "TWB64 010 - Mario Red", + "TWB64 011 - Nick Orange": "TWB64 011 - Nick Orange", + "TWB64 012 - Virtual Boy Ver.": "TWB64 012 - Virtual Boy Ver.", + "TWB64 013 - Golden Wild": "TWB64 013 - Golden Wild", + "TWB64 014 - Builder Yellow": "TWB64 014 - Builder Yellow", + "TWB64 015 - Classic Blurple": "TWB64 015 - Classic Blurple", + "TWB64 016 - 765 Production Ver.": "TWB64 016 - 765 Production Ver.", + "TWB64 017 - Superball Ivory": "TWB64 017 - Superball Ivory", + "TWB64 018 - Crunchyroll Orange": "TWB64 018 - Crunchyroll Orange", + "TWB64 019 - Muse Pink": "TWB64 019 - Muse Pink", + "TWB64 020 - Nijigasaki Yellow": "TWB64 020 - Nijigasaki Yellow", + "TWB64 021 - Gamate Ver.": "TWB64 021 - Gamate Ver.", + "TWB64 022 - Greenscale Ver.": "TWB64 022 - Greenscale Ver.", + "TWB64 023 - Odyssey Gold": "TWB64 023 - Odyssey Gold", + "TWB64 024 - Super Saiyan God": "TWB64 024 - Super Saiyan God", + "TWB64 025 - Super Saiyan Blue": "TWB64 025 - Super Saiyan Blue", + "TWB64 026 - Bizarre Pink": "TWB64 026 - Bizarre Pink", + "TWB64 027 - Nintendo Switch Lite Ver.": "TWB64 027 - Nintendo Switch Lite Ver.", + "TWB64 028 - Game.com Ver.": "TWB64 028 - Game.com Ver.", + "TWB64 029 - Sanrio Pink": "TWB64 029 - Sanrio Pink", + "TWB64 030 - BANDAI NAMCO Ver.": "TWB64 030 - BANDAI NAMCO Ver.", + "TWB64 031 - Cosmo Green": "TWB64 031 - Cosmo Green", + "TWB64 032 - Wanda Pink": "TWB64 032 - Wanda Pink", + "TWB64 033 - Link's Awakening DX Ver.": "TWB64 033 - Link's Awakening DX Ver.", + "TWB64 034 - Travel Wood": "TWB64 034 - Travel Wood", + "TWB64 035 - Pokemon Ver.": "TWB64 035 - Pokemon Ver.", + "TWB64 036 - Game Grump Orange": "TWB64 036 - Game Grump Orange", + "TWB64 037 - Scooby-Doo Mystery Ver.": "TWB64 037 - Scooby-Doo Mystery Ver.", + "TWB64 038 - Pokemon mini Ver.": "TWB64 038 - Pokemon mini Ver.", + "TWB64 039 - Supervision Ver.": "TWB64 039 - Supervision Ver.", + "TWB64 040 - DMG Ver.": "TWB64 040 - DMG Ver.", + "TWB64 041 - Pocket Ver.": "TWB64 041 - Pocket Ver.", + "TWB64 042 - Light Ver.": "TWB64 042 - Light Ver.", + "TWB64 043 - Miraitowa Blue": "TWB64 043 - Miraitowa Blue", + "TWB64 044 - Someity Pink": "TWB64 044 - Someity Pink", + "TWB64 045 - Pikachu Yellow": "TWB64 045 - Pikachu Yellow", + "TWB64 046 - Eevee Brown": "TWB64 046 - Eevee Brown", + "TWB64 047 - Microvision Ver.": "TWB64 047 - Microvision Ver.", + "TWB64 048 - TI-83 Ver.": "TWB64 048 - TI-83 Ver.", + "TWB64 049 - Aegis Cherry": "TWB64 049 - Aegis Cherry", + "TWB64 050 - Labo Fawn": "TWB64 050 - Labo Fawn", + "TWB64 051 - MILLION LIVE GOLD!": "TWB64 051 - MILLION LIVE GOLD!", + "TWB64 052 - Tokyo Midtown Ver.": "TWB64 052 - Tokyo Midtown Ver.", + "TWB64 053 - VMU Ver.": "TWB64 053 - VMU Ver.", + "TWB64 054 - Game Master Ver.": "TWB64 054 - Game Master Ver.", + "TWB64 055 - Android Green": "TWB64 055 - Android Green", + "TWB64 056 - Ticketmaster Azure": "TWB64 056 - Ticketmaster Azure", + "TWB64 057 - Google Red": "TWB64 057 - Google Red", + "TWB64 058 - Google Blue": "TWB64 058 - Google Blue", + "TWB64 059 - Google Yellow": "TWB64 059 - Google Yellow", + "TWB64 060 - Google Green": "TWB64 060 - Google Green", + "TWB64 061 - WonderSwan Ver.": "TWB64 061 - WonderSwan Ver.", + "TWB64 062 - Neo Geo Pocket Ver.": "TWB64 062 - Neo Geo Pocket Ver.", + "TWB64 063 - Dew Green": "TWB64 063 - Dew Green", + "TWB64 064 - Coca-Cola Red": "TWB64 064 - Coca-Cola Red", + "TWB64 065 - GameKing Ver.": "TWB64 065 - GameKing Ver.", + "TWB64 066 - Do The Dew Ver.": "TWB64 066 - Do The Dew Ver.", + "TWB64 067 - Digivice Ver.": "TWB64 067 - Digivice Ver.", + "TWB64 068 - Bikini Bottom Ver.": "TWB64 068 - Bikini Bottom Ver.", + "TWB64 069 - Blossom Pink": "TWB64 069 - Blossom Pink", + "TWB64 070 - Bubbles Blue": "TWB64 070 - Bubbles Blue", + "TWB64 071 - Buttercup Green": "TWB64 071 - Buttercup Green", + "TWB64 072 - NASCAR Ver.": "TWB64 072 - NASCAR Ver.", + "TWB64 073 - Lemon-Lime Green": "TWB64 073 - Lemon-Lime Green", + "TWB64 074 - Mega Man V Ver.": "TWB64 074 - Mega Man V Ver.", + "TWB64 075 - Tamagotchi Ver.": "TWB64 075 - Tamagotchi Ver.", + "TWB64 076 - Phantom Red": "TWB64 076 - Phantom Red", + "TWB64 077 - Halloween Ver.": "TWB64 077 - Halloween Ver.", + "TWB64 078 - Christmas Ver.": "TWB64 078 - Christmas Ver.", + "TWB64 079 - Cardcaptor Pink": "TWB64 079 - Cardcaptor Pink", + "TWB64 080 - Pretty Guardian Gold": "TWB64 080 - Pretty Guardian Gold", + "TWB64 081 - Camouflage Ver.": "TWB64 081 - Camouflage Ver.", + "TWB64 082 - Legendary Super Saiyan": "TWB64 082 - Legendary Super Saiyan", + "TWB64 083 - Super Saiyan Rose": "TWB64 083 - Super Saiyan Rose", + "TWB64 084 - Super Saiyan": "TWB64 084 - Super Saiyan", + "TWB64 085 - Perfected Ultra Instinct": "TWB64 085 - Perfected Ultra Instinct", + "TWB64 086 - Saint Snow Red": "TWB64 086 - Saint Snow Red", + "TWB64 087 - Yellow Banana": "TWB64 087 - Yellow Banana", + "TWB64 088 - Green Banana": "TWB64 088 - Green Banana", + "TWB64 089 - Super Saiyan 3": "TWB64 089 - Super Saiyan 3", + "TWB64 090 - Super Saiyan Blue Evolved": "TWB64 090 - Super Saiyan Blue Evolved", + "TWB64 091 - Pocket Tales Ver.": "TWB64 091 - Pocket Tales Ver.", + "TWB64 092 - Investigation Yellow": "TWB64 092 - Investigation Yellow", + "TWB64 093 - S.E.E.S. Blue": "TWB64 093 - S.E.E.S. Blue", + "TWB64 094 - Game Awards Cyan": "TWB64 094 - Game Awards Cyan", + "TWB64 095 - Hokage Orange": "TWB64 095 - Hokage Orange", + "TWB64 096 - Straw Hat Red": "TWB64 096 - Straw Hat Red", + "TWB64 097 - Sword Art Cyan": "TWB64 097 - Sword Art Cyan", + "TWB64 098 - Deku Alpha Emerald": "TWB64 098 - Deku Alpha Emerald", + "TWB64 099 - Blue Stripes Ver.": "TWB64 099 - Blue Stripes Ver.", + "TWB64 100 - Stone Orange": "TWB64 100 - Stone Orange", + "gambatte gb palette twb64 1": "gambatte gb palette twb64 1", + "TWB64 101 - 765PRO Pink": "TWB64 101 - 765PRO Pink", + "TWB64 102 - CINDERELLA Blue": "TWB64 102 - CINDERELLA Blue", + "TWB64 103 - MILLION Yellow!": "TWB64 103 - MILLION Yellow!", + "TWB64 104 - SideM Green": "TWB64 104 - SideM Green", + "TWB64 105 - SHINY Sky Blue": "TWB64 105 - SHINY Sky Blue", + "TWB64 106 - Angry Volcano Ver.": "TWB64 106 - Angry Volcano Ver.", + "TWB64 107 - Yo-kai Pink": "TWB64 107 - Yo-kai Pink", + "TWB64 108 - Yo-kai Green": "TWB64 108 - Yo-kai Green", + "TWB64 109 - Yo-kai Blue": "TWB64 109 - Yo-kai Blue", + "TWB64 110 - Yo-kai Purple": "TWB64 110 - Yo-kai Purple", + "TWB64 111 - Aquatic Iro": "TWB64 111 - Aquatic Iro", + "TWB64 112 - Tea Midori": "TWB64 112 - Tea Midori", + "TWB64 113 - Sakura Pink": "TWB64 113 - Sakura Pink", + "TWB64 114 - Wisteria Murasaki": "TWB64 114 - Wisteria Murasaki", + "TWB64 115 - Oni Aka": "TWB64 115 - Oni Aka", + "TWB64 116 - Golden Kiiro": "TWB64 116 - Golden Kiiro", + "TWB64 117 - Silver Shiro": "TWB64 117 - Silver Shiro", + "TWB64 118 - Fruity Orange": "TWB64 118 - Fruity Orange", + "TWB64 119 - AKB48 Pink": "TWB64 119 - AKB48 Pink", + "TWB64 120 - Miku Blue": "TWB64 120 - Miku Blue", + "TWB64 121 - Fairy Tail Red": "TWB64 121 - Fairy Tail Red", + "TWB64 122 - Survey Corps Brown": "TWB64 122 - Survey Corps Brown", + "TWB64 123 - Island Green": "TWB64 123 - Island Green", + "TWB64 124 - Mania Plus Green": "TWB64 124 - Mania Plus Green", + "TWB64 125 - Ninja Turtle Green": "TWB64 125 - Ninja Turtle Green", + "TWB64 126 - Slime Blue": "TWB64 126 - Slime Blue", + "TWB64 127 - Lime Midori": "TWB64 127 - Lime Midori", + "TWB64 128 - Ghostly Aoi": "TWB64 128 - Ghostly Aoi", + "TWB64 129 - Retro Bogeda": "TWB64 129 - Retro Bogeda", + "TWB64 130 - Royal Blue": "TWB64 130 - Royal Blue", + "TWB64 131 - Neon Purple": "TWB64 131 - Neon Purple", + "TWB64 132 - Neon Orange": "TWB64 132 - Neon Orange", + "TWB64 133 - Moonlight Vision": "TWB64 133 - Moonlight Vision", + "TWB64 134 - Tokyo Red": "TWB64 134 - Tokyo Red", + "TWB64 135 - Paris Gold": "TWB64 135 - Paris Gold", + "TWB64 136 - Beijing Blue": "TWB64 136 - Beijing Blue", + "TWB64 137 - Pac-Man Yellow": "TWB64 137 - Pac-Man Yellow", + "TWB64 138 - Irish Green": "TWB64 138 - Irish Green", + "TWB64 139 - Kakarot Orange": "TWB64 139 - Kakarot Orange", + "TWB64 140 - Dragon Ball Orange": "TWB64 140 - Dragon Ball Orange", + "TWB64 141 - Christmas Gold": "TWB64 141 - Christmas Gold", + "TWB64 142 - Pepsi-Cola Blue": "TWB64 142 - Pepsi-Cola Blue", + "TWB64 143 - Bubblun Green": "TWB64 143 - Bubblun Green", + "TWB64 144 - Bobblun Blue": "TWB64 144 - Bobblun Blue", + "TWB64 145 - Baja Blast Storm": "TWB64 145 - Baja Blast Storm", + "TWB64 146 - Olympic Gold": "TWB64 146 - Olympic Gold", + "TWB64 147 - Value Orange": "TWB64 147 - Value Orange", + "TWB64 148 - Liella Purple!": "TWB64 148 - Liella Purple!", + "TWB64 149 - Olympic Silver": "TWB64 149 - Olympic Silver", + "TWB64 150 - Olympic Bronze": "TWB64 150 - Olympic Bronze", + "TWB64 151 - ANA Sky Blue": "TWB64 151 - ANA Sky Blue", + "TWB64 152 - Nijigasaki Orange": "TWB64 152 - Nijigasaki Orange", + "TWB64 153 - HoloBlue": "TWB64 153 - HoloBlue", + "TWB64 154 - Wrestling Red": "TWB64 154 - Wrestling Red", + "TWB64 155 - Yoshi Egg Green": "TWB64 155 - Yoshi Egg Green", + "TWB64 156 - Pokedex Red": "TWB64 156 - Pokedex Red", + "TWB64 157 - Disney Dream Blue": "TWB64 157 - Disney Dream Blue", + "TWB64 158 - Xbox Green": "TWB64 158 - Xbox Green", + "TWB64 159 - Sonic Mega Blue": "TWB64 159 - Sonic Mega Blue", + "TWB64 160 - G4 Orange": "TWB64 160 - G4 Orange", + "TWB64 161 - Scarlett Green": "TWB64 161 - Scarlett Green", + "TWB64 162 - Glitchy Blue": "TWB64 162 - Glitchy Blue", + "TWB64 163 - Classic LCD": "TWB64 163 - Classic LCD", + "TWB64 164 - 3DS Virtual Console Ver.": "TWB64 164 - 3DS Virtual Console Ver.", + "TWB64 165 - PocketStation Ver.": "TWB64 165 - PocketStation Ver.", + "TWB64 166 - Game and Gold": "TWB64 166 - Game and Gold", + "TWB64 167 - Smurfy Blue": "TWB64 167 - Smurfy Blue", + "TWB64 168 - Swampy Ogre Green": "TWB64 168 - Swampy Ogre Green", + "TWB64 169 - Sailor Spinach Green": "TWB64 169 - Sailor Spinach Green", + "TWB64 170 - Shenron Green": "TWB64 170 - Shenron Green", + "TWB64 171 - Berserk Blood": "TWB64 171 - Berserk Blood", + "TWB64 172 - Super Star Pink": "TWB64 172 - Super Star Pink", + "TWB64 173 - Gamebuino Classic Ver.": "TWB64 173 - Gamebuino Classic Ver.", + "TWB64 174 - Barbie Pink": "TWB64 174 - Barbie Pink", + "TWB64 175 - Star Command Green": "TWB64 175 - Star Command Green", + "TWB64 176 - Nokia 3310 Ver.": "TWB64 176 - Nokia 3310 Ver.", + "TWB64 177 - Clover Green": "TWB64 177 - Clover Green", + "TWB64 178 - Crash Orange": "TWB64 178 - Crash Orange", + "TWB64 179 - Famicom Disk Yellow": "TWB64 179 - Famicom Disk Yellow", + "TWB64 180 - Team Rocket Red": "TWB64 180 - Team Rocket Red", + "TWB64 181 - SEIKO Timer Yellow": "TWB64 181 - SEIKO Timer Yellow", + "TWB64 182 - PINK109": "TWB64 182 - PINK109", + "TWB64 183 - Doraemon Blue": "TWB64 183 - Doraemon Blue", + "TWB64 184 - Fury Blue": "TWB64 184 - Fury Blue", + "TWB64 185 - Rockstar Orange": "TWB64 185 - Rockstar Orange", + "TWB64 186 - Puyo Puyo Green": "TWB64 186 - Puyo Puyo Green", + "TWB64 187 - Susan G. Pink": "TWB64 187 - Susan G. Pink", + "TWB64 188 - Pizza Hut Red": "TWB64 188 - Pizza Hut Red", + "TWB64 189 - Plumbob Green": "TWB64 189 - Plumbob Green", + "TWB64 190 - Grand Ivory": "TWB64 190 - Grand Ivory", + "TWB64 191 - Demon's Gold": "TWB64 191 - Demon's Gold", + "TWB64 192 - SEGA Tokyo Blue": "TWB64 192 - SEGA Tokyo Blue", + "TWB64 193 - Champion Blue": "TWB64 193 - Champion Blue", + "TWB64 194 - DK Barrel Brown": "TWB64 194 - DK Barrel Brown", + "TWB64 195 - Evangelion Green": "TWB64 195 - Evangelion Green", + "TWB64 196 - Equestrian Purple": "TWB64 196 - Equestrian Purple", + "TWB64 197 - Autobot Red": "TWB64 197 - Autobot Red", + "TWB64 198 - Niconico Sea Green": "TWB64 198 - Niconico Sea Green", + "TWB64 199 - Duracell Copper": "TWB64 199 - Duracell Copper", + "TWB64 200 - TOKYO SKYTREE CLOUDY BLUE": "TWB64 200 - TOKYO SKYTREE CLOUDY BLUE", + "gambatte gb palette twb64 2": "gambatte gb palette twb64 2", + "PixelShift 01 - Arctic Green": "PixelShift 01 - Arctic Green", + "PixelShift 02 - Arduboy": "PixelShift 02 - Arduboy", + "PixelShift 03 - BGB 0.3 Emulator": "PixelShift 03 - BGB 0.3 Emulator", + "PixelShift 04 - Camouflage": "PixelShift 04 - Camouflage", + "PixelShift 05 - Chocolate Bar": "PixelShift 05 - Chocolate Bar", + "PixelShift 06 - CMYK": "PixelShift 06 - CMYK", + "PixelShift 07 - Cotton Candy": "PixelShift 07 - Cotton Candy", + "PixelShift 08 - Easy Greens": "PixelShift 08 - Easy Greens", + "PixelShift 09 - Gamate": "PixelShift 09 - Gamate", + "PixelShift 10 - Game Boy Light": "PixelShift 10 - Game Boy Light", + "PixelShift 11 - Game Boy Pocket": "PixelShift 11 - Game Boy Pocket", + "PixelShift 12 - Game Boy Pocket Alt": "PixelShift 12 - Game Boy Pocket Alt", + "PixelShift 13 - Game Pocket Computer": "PixelShift 13 - Game Pocket Computer", + "PixelShift 14 - Game & Watch Ball": "PixelShift 14 - Game & Watch Ball", + "PixelShift 15 - GB Backlight Blue": "PixelShift 15 - GB Backlight Blue", + "PixelShift 16 - GB Backlight Faded": "PixelShift 16 - GB Backlight Faded", + "PixelShift 17 - GB Backlight Orange": "PixelShift 17 - GB Backlight Orange", + "PixelShift 18 - GB Backlight White ": "PixelShift 18 - GB Backlight White ", + "PixelShift 19 - GB Backlight Yellow Dark": "PixelShift 19 - GB Backlight Yellow Dark", + "PixelShift 20 - GB Bootleg": "PixelShift 20 - GB Bootleg", + "PixelShift 21 - GB Hunter": "PixelShift 21 - GB Hunter", + "PixelShift 22 - GB Kiosk": "PixelShift 22 - GB Kiosk", + "PixelShift 23 - GB Kiosk 2": "PixelShift 23 - GB Kiosk 2", + "PixelShift 24 - GB New": "PixelShift 24 - GB New", + "PixelShift 25 - GB Nuked": "PixelShift 25 - GB Nuked", + "PixelShift 26 - GB Old": "PixelShift 26 - GB Old", + "PixelShift 27 - GBP Bivert": "PixelShift 27 - GBP Bivert", + "PixelShift 28 - GB Washed Yellow Backlight": "PixelShift 28 - GB Washed Yellow Backlight", + "PixelShift 29 - Ghost": "PixelShift 29 - Ghost", + "PixelShift 30 - Glow In The Dark": "PixelShift 30 - Glow In The Dark", + "PixelShift 31 - Gold Bar": "PixelShift 31 - Gold Bar", + "PixelShift 32 - Grapefruit": "PixelShift 32 - Grapefruit", + "PixelShift 33 - Gray Green Mix": "PixelShift 33 - Gray Green Mix", + "PixelShift 34 - Missingno": "PixelShift 34 - Missingno", + "PixelShift 35 - MS-Dos": "PixelShift 35 - MS-Dos", + "PixelShift 36 - Newspaper": "PixelShift 36 - Newspaper", + "PixelShift 37 - Pip-Boy": "PixelShift 37 - Pip-Boy", + "PixelShift 38 - Pocket Girl": "PixelShift 38 - Pocket Girl", + "PixelShift 39 - Silhouette": "PixelShift 39 - Silhouette", + "PixelShift 40 - Sunburst": "PixelShift 40 - Sunburst", + "PixelShift 41 - Technicolor": "PixelShift 41 - Technicolor", + "PixelShift 42 - Tron": "PixelShift 42 - Tron", + "PixelShift 43 - Vaporwave": "PixelShift 43 - Vaporwave", + "PixelShift 44 - Virtual Boy": "PixelShift 44 - Virtual Boy", + "PixelShift 45 - Wish": "PixelShift 45 - Wish", + "gambatte gb palette pixelshift 1": "gambatte gb palette pixelshift 1", + "gambatte dark filter level": "gambatte dark filter level", + "GB": "GB", + "GBC": "GBC", + "GBA": "GBA", + "gambatte gb hwmode": "gambatte gb hwmode", + "gambatte turbo period": "gambatte turbo period", + "gambatte rumble level": "gambatte rumble level", + "pcsx rearmed psxclock": "pcsx rearmed psxclock", + "pcsx rearmed frameskip threshold": "pcsx rearmed frameskip threshold", + "pcsx rearmed frameskip interval": "pcsx rearmed frameskip interval", + "pcsx rearmed input sensitivity": "pcsx rearmed input sensitivity", + "pcsx rearmed gunconadjustx": "pcsx rearmed gunconadjustx", + "pcsx rearmed gunconadjusty": "pcsx rearmed gunconadjusty", + "pcsx rearmed gunconadjustratiox": "pcsx rearmed gunconadjustratiox", + "pcsx rearmed gunconadjustratioy": "pcsx rearmed gunconadjustratioy", + "Japan NTSC": "Japan NTSC", + "Japan PAL": "Japan PAL", + "US": "US", + "Europe": "Europe", + "picodrive region": "picodrive region", + "Game Gear": "Game Gear", + "Master System": "Master System", + "SG-1000": "SG-1000", + "SC-3000": "SC-3000", + "picodrive smstype": "picodrive smstype", + "Sega": "Sega", + "Codemasters": "Codemasters", + "Korea": "Korea", + "Korea MSX": "Korea MSX", + "Korea X-in-1": "Korea X-in-1", + "Korea 4-Pak": "Korea 4-Pak", + "Korea Janggun": "Korea Janggun", + "Korea Nemesis": "Korea Nemesis", + "Taiwan 8K RAM": "Taiwan 8K RAM", + "picodrive smsmapper": "picodrive smsmapper", + "PAR": "PAR", + "CRT": "CRT", + "picodrive aspect": "picodrive aspect", + "native": "native", + "picodrive sound rate": "picodrive sound rate", + "picodrive lowpass range": "picodrive lowpass range", + "picodrive frameskip threshold": "picodrive frameskip threshold", + "genesis plus gx frameskip threshold": "genesis plus gx frameskip threshold", + "genesis plus gx lowpass range": "genesis plus gx lowpass range", + "genesis plus gx psg preamp": "genesis plus gx psg preamp", + "genesis plus gx fm preamp": "genesis plus gx fm preamp", + "genesis plus gx cdda volume": "genesis plus gx cdda volume", + "genesis plus gx pcm volume": "genesis plus gx pcm volume", + "genesis plus gx audio eq low": "genesis plus gx audio eq low", + "genesis plus gx audio eq mid": "genesis plus gx audio eq mid", + "genesis plus gx audio eq high": "genesis plus gx audio eq high", + "genesis plus gx enhanced vscroll limit": "genesis plus gx enhanced vscroll limit", + "genesis plus gx psg channel 0 volume": "genesis plus gx psg channel 0 volume", + "genesis plus gx psg channel 1 volume": "genesis plus gx psg channel 1 volume", + "genesis plus gx psg channel 2 volume": "genesis plus gx psg channel 2 volume", + "genesis plus gx psg channel 3 volume": "genesis plus gx psg channel 3 volume", + "genesis plus gx md channel 0 volume": "genesis plus gx md channel 0 volume", + "genesis plus gx md channel 1 volume": "genesis plus gx md channel 1 volume", + "genesis plus gx md channel 2 volume": "genesis plus gx md channel 2 volume", + "genesis plus gx md channel 3 volume": "genesis plus gx md channel 3 volume", + "genesis plus gx md channel 4 volume": "genesis plus gx md channel 4 volume", + "genesis plus gx md channel 5 volume": "genesis plus gx md channel 5 volume", + "genesis plus gx sms fm channel 0 volume": "genesis plus gx sms fm channel 0 volume", + "genesis plus gx sms fm channel 1 volume": "genesis plus gx sms fm channel 1 volume", + "genesis plus gx sms fm channel 2 volume": "genesis plus gx sms fm channel 2 volume", + "genesis plus gx sms fm channel 3 volume": "genesis plus gx sms fm channel 3 volume", + "genesis plus gx sms fm channel 4 volume": "genesis plus gx sms fm channel 4 volume", + "genesis plus gx sms fm channel 5 volume": "genesis plus gx sms fm channel 5 volume", + "genesis plus gx sms fm channel 6 volume": "genesis plus gx sms fm channel 6 volume", + "genesis plus gx sms fm channel 7 volume": "genesis plus gx sms fm channel 7 volume", + "genesis plus gx sms fm channel 8 volume": "genesis plus gx sms fm channel 8 volume", + "anaglyph": "anaglyph", + "cyberscope": "cyberscope", + "side-by-side": "side-by-side", + "vli": "vli", + "hli": "hli", + "vb 3dmode": "vb 3dmode", + "black & red": "black & red", + "black & white": "black & white", + "black & blue": "black & blue", + "black & cyan": "black & cyan", + "black & electric cyan": "black & electric cyan", + "black & green": "black & green", + "black & magenta": "black & magenta", + "black & yellow": "black & yellow", + "vb color mode": "vb color mode", + "accurate": "accurate", + "fast": "fast", + "vb cpu emulation": "vb cpu emulation" +} From 2dd1a3df5c6ef691f7b3db1af60ddd24052384f0 Mon Sep 17 00:00:00 2001 From: Allan Niles Date: Mon, 28 Apr 2025 12:50:23 -0600 Subject: [PATCH 016/106] Add context menu and fix virtualGamepad button --- data/emulator.css | 5 +++-- data/src/emulator.js | 44 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/data/emulator.css b/data/emulator.css index 33f087d4d..7349e2916 100644 --- a/data/emulator.css +++ b/data/emulator.css @@ -1337,14 +1337,15 @@ .ejs_virtualGamepad_open { display: inline-block; - width: 30px; - height: 30px; + width: 20px; + height: 20px; color: #fff; position: absolute; top: 5px; right: 5px; opacity: .5; z-index: 999; + cursor: pointer; } .ejs_virtualGamepad_open svg { fill: currentColor; diff --git a/data/src/emulator.js b/data/src/emulator.js index 9e8795225..80dede8fc 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -1171,22 +1171,45 @@ class EmulatorJS { createContextMenu() { this.elements.contextmenu = this.createElement('div'); this.elements.contextmenu.classList.add("ejs_context_menu"); - this.addEventListener(this.game, 'contextmenu', (e) => { - e.preventDefault(); - if ((this.config.buttonOpts && this.config.buttonOpts.rightClick === false) || !this.started) return; + const showMenu = (e) => { const parentRect = this.elements.parent.getBoundingClientRect(); this.elements.contextmenu.style.display = "block"; const rect = this.elements.contextmenu.getBoundingClientRect(); - const up = e.offsetY + rect.height > parentRect.bottom - 25; - const left = e.offsetX + rect.width > parentRect.right - 5; - this.elements.contextmenu.style.left = (e.offsetX - (left ? rect.width : 0)) + "px"; - this.elements.contextmenu.style.top = (e.offsetY - (up ? rect.height : 0)) + "px"; + const up = (e.offsetY || e.touches[0].clientY) + rect.height > parentRect.bottom - 25; + const left = (e.offsetX || e.touches[0].clientX) + rect.width > parentRect.right - 5; + this.elements.contextmenu.style.left = ((e.offsetX || e.touches[0].clientX) - (left ? rect.width : 0)) + "px"; + this.elements.contextmenu.style.top = ((e.offsetY || e.touches[0].clientY) - (up ? rect.height : 0)) + "px"; + } + this.addEventListener(this.game, 'contextmenu', (e) => { + e.preventDefault(); + if ((this.config.buttonOpts && this.config.buttonOpts.rightClick === false) || !this.started) return; + showMenu(e); }) const hideMenu = () => { this.elements.contextmenu.style.display = "none"; } this.addEventListener(this.elements.contextmenu, 'contextmenu', (e) => e.preventDefault()); this.addEventListener(this.elements.parent, 'contextmenu', (e) => e.preventDefault()); + let holdTimer; + let touchHold = false; + this.addEventListener(this.game, 'touchstart', (e) => { + holdTimer = setTimeout(() => { + if (this.elements.contextmenu.style.display === "none") { + showMenu(e); + touchHold = true; + setTimeout(this.menu.close.bind(this), 20); + } + }, 750) + }); + this.addEventListener(this.game, 'touchend', () => { + clearTimeout(holdTimer); + if (touchHold) { + touchHold = false; + return; + } + hideMenu(); + }); + this.addEventListener(this.game, 'touchmove touchcancel', clearTimeout(holdTimer)); this.addEventListener(this.game, 'mousedown', hideMenu); const parent = this.createElement("ul"); const addButton = (title, hidden, functi0n) => { @@ -3765,7 +3788,11 @@ class EmulatorJS { menuButton.innerHTML = ''; menuButton.classList.add("ejs_virtualGamepad_open"); menuButton.style.display = "none"; - this.on("start", () => menuButton.style.display = ""); + this.on("start", () => { + if (this.settings["virtual-gamepad"] !== "disabled" || this.isMobile) { + menuButton.style.display = "" + } + }); this.elements.parent.appendChild(menuButton); let timeout; let ready = true; @@ -3917,6 +3944,7 @@ class EmulatorJS { this.gameManager.setCurrentDisk(value); } else if (option === "virtual-gamepad") { this.toggleVirtualGamepad(value !== "disabled"); + this.elements.menuToggle.style.display = (value !== "disabled" || this.isMobile) ? "" : "none"; } else if (option === "virtual-gamepad-left-handed-mode") { this.toggleVirtualGamepadLeftHanded(value !== "disabled"); } else if (option === "ff-ratio") { From 1b655b9e4da07094827fdd8013f179eb7bab0a1f Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:39:16 +1000 Subject: [PATCH 017/106] Enhance toolbar customisation options (#992) * Proof of concept * POC now implements custom buttons (up to three) * Reversed formatting in loader.js * Reversed formatting changes for emulator.js * Revert formatting changes to index.html * Revert whitespace formatting * Migrated button defaults and setup code to emulator.js * Some more refactoring * Unlimited custom buttons * Remove demo code from index.html --- data/src/emulator.js | 286 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 241 insertions(+), 45 deletions(-) diff --git a/data/src/emulator.js b/data/src/emulator.js index 80dede8fc..85b2c74f9 100644 --- a/data/src/emulator.js +++ b/data/src/emulator.js @@ -207,6 +207,7 @@ class EmulatorJS { if (this.debug || (window.location && ['localhost', '127.0.0.1'].includes(location.hostname))) this.checkForUpdates(); this.netplayEnabled = (window.EJS_DEBUG_XX === true) && (window.EJS_EXPERIMENTAL_NETPLAY === true); this.config = config; + this.config.buttonOpts = this.buildButtonOptions(this.config.buttonOpts); this.config.settingsLanguage = window.EJS_settingsLanguage || false; this.currentPopup = null; this.isFastForward = false; @@ -1168,6 +1169,186 @@ class EmulatorJS { elem.appendChild(elm); } } + defaultButtonOptions = { + playPause: { + visible: true, + icon: "play", + displayName: "Play/Pause" + }, + play: { + visible: true, + icon: '', + displayName: "Play" + }, + pause: { + visible: true, + icon: '', + displayName: "Pause" + }, + restart: { + visible: true, + icon: '', + displayName: "Restart" + }, + mute: { + visible: true, + icon: '', + displayName: "Mute" + }, + unmute: { + visible: true, + icon: '', + displayName: "Unmute" + }, + settings: { + visible: true, + icon: '', + displayName: "Settings" + }, + fullscreen: { + visible: true, + icon: "fullscreen", + displayName: "Fullscreen" + }, + enterFullscreen: { + visible: true, + icon: '', + displayName: "Enter Fullscreen" + }, + exitFullscreen: { + visible: true, + icon: '', + displayName: "Exit Fullscreen" + }, + saveState: { + visible: true, + icon: '', + displayName: "Save State" + }, + loadState: { + visible: true, + icon: '', + displayName: "Load State" + }, + screenRecord: { + visible: true + }, + gamepad: { + visible: true, + icon: '', + displayName: "Control Settings" + }, + cheat: { + visible: true, + icon: '', + displayName: "Cheats" + }, + volumeSlider: { + visible: true + }, + saveSavFiles: { + visible: true, + icon: '', + displayName: "Export Save File" + }, + loadSavFiles: { + visible: true, + icon: '', + displayName: "Import Save File" + }, + quickSave: { + visible: true + }, + quickLoad: { + visible: true + }, + screenshot: { + visible: true + }, + cacheManager: { + visible: true, + icon: '', + displayName: "Cache Manager" + }, + exitEmulation: { + visible: true, + icon: '', + displayName: "Exit Emulation" + }, + netplay: { + visible: false, + displayName: "Netplay", + icon: '' + }, + diskButton: { + visible: true, + icon: '', + displayName: "Disks" + }, + contextMenu: { + visible: true, + displayName: "Context Menu", + icon: '' + } + }; + buildButtonOptions(buttonUserOpts) { + let mergedButtonOptions = this.defaultButtonOptions; + + // merge buttonUserOpts with mergedButtonOptions + if (buttonUserOpts) { + for (const key in buttonUserOpts) { + // Check if the button exists in the default buttons, and update its properties + // if the value is a boolean, set the visible property to the value + if (typeof buttonUserOpts[key] === "boolean") { + mergedButtonOptions[key].visible = buttonUserOpts[key]; + } else if (typeof buttonUserOpts[key] === "object") { + // If the value is an object, merge it with the default button properties + + if (this.defaultButtonOptions[key]) { + // copy properties from the button definition if they aren't null + for (const prop in buttonUserOpts[key]) { + if (buttonUserOpts[key][prop] !== null) { + mergedButtonOptions[key][prop] = buttonUserOpts[key][prop]; + } + } + } else { + // button was not in the default buttons list and is therefore a custom button + // verify that the value has a displayName, icon, and callback property + if (buttonUserOpts[key].displayName && buttonUserOpts[key].icon && buttonUserOpts[key].callback) { + mergedButtonOptions[key] = { + visible: true, + displayName: buttonUserOpts[key].displayName, + icon: buttonUserOpts[key].icon, + callback: buttonUserOpts[key].callback, + custom: true + }; + } else { + console.warn(`Custom button "${key}" is missing required properties`); + } + } + } + + // behaviour exceptions + switch (key) { + case "playPause": + mergedButtonOptions.play.visible = mergedButtonOptions.playPause.visible; + mergedButtonOptions.pause.visible = mergedButtonOptions.playPause.visible; + break; + + case "mute": + mergedButtonOptions.unmute.visible = mergedButtonOptions.mute.visible; + break; + + case "fullscreen": + mergedButtonOptions.enterFullscreen.visible = mergedButtonOptions.fullscreen.visible; + mergedButtonOptions.exitFullscreen.visible = mergedButtonOptions.fullscreen.visible; + break; + } + } + } + + return mergedButtonOptions; + } createContextMenu() { this.elements.contextmenu = this.createElement('div'); this.elements.contextmenu.classList.add("ejs_context_menu"); @@ -1402,10 +1583,10 @@ class EmulatorJS { }); if (this.config.buttonOpts) { - if (this.config.buttonOpts.screenshot === false) screenshot.setAttribute("hidden", ""); - if (this.config.buttonOpts.screenRecord === false) startScreenRecording.setAttribute("hidden", ""); - if (this.config.buttonOpts.quickSave === false) qSave.setAttribute("hidden", ""); - if (this.config.buttonOpts.quickLoad === false) qLoad.setAttribute("hidden", ""); + if (this.config.buttonOpts.screenshot.visible === false) screenshot.setAttribute("hidden", ""); + if (this.config.buttonOpts.screenRecord.visible === false) startScreenRecording.setAttribute("hidden", ""); + if (this.config.buttonOpts.quickSave.visible === false) qSave.setAttribute("hidden", ""); + if (this.config.buttonOpts.quickLoad.visible === false) qLoad.setAttribute("hidden", ""); } this.elements.contextmenu.appendChild(parent); @@ -1548,15 +1729,15 @@ class EmulatorJS { let paddingSet = false; //Now add buttons - const addButton = (title, image, callback, element, both) => { + const addButton = (buttonConfig, callback, element, both) => { const button = this.createElement("button"); button.type = "button"; const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute("role", "presentation"); svg.setAttribute("focusable", "false"); - svg.innerHTML = image; + svg.innerHTML = buttonConfig.icon; const text = this.createElement("span"); - text.innerText = this.localization(title); + text.innerText = this.localization(buttonConfig.displayName); if (paddingSet) text.classList.add("ejs_menu_text_right"); text.classList.add("ejs_menu_text"); @@ -1571,10 +1752,14 @@ class EmulatorJS { if (callback instanceof Function) { this.addEventListener(button, 'click', callback); } + + if (buttonConfig.callback instanceof Function) { + this.addEventListener(button, 'click', buttonConfig.callback); + } return both ? [button, svg, text] : button; } - const restartButton = addButton("Restart", '', () => { + const restartButton = addButton(this.config.buttonOpts.restart, () => { if (this.isNetplay && this.netplay.owner) { this.gameManager.restart(); this.netplay.reset(); @@ -1584,7 +1769,7 @@ class EmulatorJS { this.gameManager.restart(); } }); - const pauseButton = addButton("Pause", '', () => { + const pauseButton = addButton(this.config.buttonOpts.pause, () => { if (this.isNetplay && this.netplay.owner) { this.pause(); this.gameManager.saveSaveFiles(); @@ -1593,7 +1778,7 @@ class EmulatorJS { this.pause(); } }); - const playButton = addButton("Play", '', () => { + const playButton = addButton(this.config.buttonOpts.play, () => { if (this.isNetplay && this.netplay.owner) { this.play(); this.netplay.sendMessage({play:true}); @@ -1632,7 +1817,7 @@ class EmulatorJS { } let stateUrl; - const saveState = addButton("Save State", '', async () => { + const saveState = addButton(this.config.buttonOpts.saveState, async () => { const state = this.gameManager.getState(); const called = this.callEvent("saveState", { screenshot: await this.gameManager.screenshot(), @@ -1652,7 +1837,7 @@ class EmulatorJS { a.click(); } }); - const loadState = addButton("Load State", '', async () => { + const loadState = addButton(this.config.buttonOpts.loadState, async () => { const called = this.callEvent("loadState"); if (called > 0) return; if (this.settings['save-state-location'] === "browser" && this.saveInBrowserSupported()) { @@ -1666,21 +1851,21 @@ class EmulatorJS { this.gameManager.loadState(state); } }); - const controlMenu = addButton("Control Settings", '', () => { + const controlMenu = addButton(this.config.buttonOpts.gamepad, () => { this.controlMenu.style.display = ""; }); - const cheatMenu = addButton("Cheats", '', () => { + const cheatMenu = addButton(this.config.buttonOpts.cheat, () => { this.cheatMenu.style.display = ""; }); - - const cache = addButton("Cache Manager", '', () => { + + const cache = addButton(this.config.buttonOpts.cacheManager, () => { this.openCacheMenu(); }); if (this.config.disableDatabases) cache.style.display = "none"; let savUrl; - - const saveSavFiles = addButton("Export Save File", '', async () => { + + const saveSavFiles = addButton(this.config.buttonOpts.saveSavFiles, async () => { const file = await this.gameManager.getSaveFile(); const called = this.callEvent("saveSave", { screenshot: await this.gameManager.screenshot(), @@ -1694,7 +1879,7 @@ class EmulatorJS { a.download = this.gameManager.getSaveFilePath().split("/").pop(); a.click(); }); - const loadSavFiles = addButton("Import Save File", '', async () => { + const loadSavFiles = addButton(this.config.buttonOpts.loadSavFiles, async () => { const called = this.callEvent("loadSave"); if (called > 0) return; const file = await this.selectFile(); @@ -1711,10 +1896,20 @@ class EmulatorJS { this.gameManager.FS.writeFile(path, sav); this.gameManager.loadSaveFiles(); }); - const netplay = addButton("Netplay", '', async () => { + const netplay = addButton(this.config.buttonOpts.netplay, async () => { this.openNetplayMenu(); }); + // add custom buttons + // get all elements from this.config.buttonOpts with custom: true + if (this.config.buttonOpts) { + for (const [key, value] of Object.entries(this.config.buttonOpts)) { + if (value.custom === true) { + const customBtn = addButton(value); + } + } + } + const spacer = this.createElement("span"); spacer.classList.add("ejs_menu_bar_spacer"); this.elements.menu.appendChild(spacer); @@ -1722,13 +1917,13 @@ class EmulatorJS { const volumeSettings = this.createElement("div"); volumeSettings.classList.add("ejs_volume_parent"); - const muteButton = addButton("Mute", '', () => { + const muteButton = addButton(this.config.buttonOpts.mute, () => { muteButton.style.display = "none"; unmuteButton.style.display = ""; this.muted = true; this.setVolume(0); }, volumeSettings); - const unmuteButton = addButton("Unmute", '', () => { + const unmuteButton = addButton(this.config.buttonOpts.unmute, () => { if (this.volume === 0) this.volume = 0.5; muteButton.style.display = ""; unmuteButton.style.display = "none"; @@ -1782,7 +1977,7 @@ class EmulatorJS { this.elements.menu.appendChild(volumeSettings); - const contextMenuButton = addButton("Context Menu", '', () => { + const contextMenuButton = addButton(this.config.buttonOpts.contextMenu, () => { if (this.elements.contextmenu.style.display === "none") { this.elements.contextmenu.style.display = "block"; this.elements.contextmenu.style.left = (getComputedStyle(this.elements.parent).width.split("px")[0]/2 - getComputedStyle(this.elements.contextmenu).width.split("px")[0]/2)+"px"; @@ -1796,7 +1991,7 @@ class EmulatorJS { this.diskParent = this.createElement("div"); this.diskParent.id = "ejs_disksMenu"; this.disksMenuOpen = false; - const diskButton = addButton("Disks", '', () => { + const diskButton = addButton(this.config.buttonOpts.diskButton, () => { this.disksMenuOpen = !this.disksMenuOpen; diskButton[1].classList.toggle("ejs_svg_rotate", this.disksMenuOpen); this.disksMenu.style.display = this.disksMenuOpen ? "" : "none"; @@ -1819,7 +2014,7 @@ class EmulatorJS { this.settingParent = this.createElement("div"); this.settingsMenuOpen = false; - const settingButton = addButton("Settings", '', () => { + const settingButton = addButton(this.config.buttonOpts.settings, () => { this.settingsMenuOpen = !this.settingsMenuOpen; settingButton[1].classList.toggle("ejs_svg_rotate", this.settingsMenuOpen); this.settingsMenu.style.display = this.settingsMenuOpen ? "" : "none"; @@ -1851,11 +2046,11 @@ class EmulatorJS { this.menu.close(); } }) - - const enter = addButton("Enter Fullscreen", '', () => { + + const enter = addButton(this.config.buttonOpts.enterFullscreen, () => { this.toggleFullscreen(true); }); - const exit = addButton("Exit Fullscreen", '', () => { + const exit = addButton(this.config.buttonOpts.exitFullscreen, () => { this.toggleFullscreen(false); }); exit.style.display = "none"; @@ -1899,7 +2094,7 @@ class EmulatorJS { } let exitMenuIsOpen = false; - const exitEmulation = addButton("Exit EmulatorJS", '', async () => { + const exitEmulation = addButton(this.config.buttonOpts.exitEmulation, async () => { if (exitMenuIsOpen) return; exitMenuIsOpen = true; const popups = this.createSubPopup(); @@ -1993,32 +2188,33 @@ class EmulatorJS { if (this.config.buttonOpts) { if (this.debug) console.log(this.config.buttonOpts); - if (this.config.buttonOpts.playPause === false) { + if (this.config.buttonOpts.playPause.visible === false) { pauseButton.style.display = "none"; playButton.style.display = "none"; } if (this.config.buttonOpts.contextMenuButton === false && this.config.buttonOpts.rightClick !== false && this.isMobile === false) contextMenuButton.style.display = "none" - if (this.config.buttonOpts.restart === false) restartButton.style.display = "none" - if (this.config.buttonOpts.settings === false) settingButton[0].style.display = "none" - if (this.config.buttonOpts.fullscreen === false) { + if (this.config.buttonOpts.restart.visible === false) restartButton.style.display = "none" + if (this.config.buttonOpts.settings.visible === false) settingButton[0].style.display = "none" + if (this.config.buttonOpts.fullscreen.visible === false) { enter.style.display = "none"; exit.style.display = "none"; } - if (this.config.buttonOpts.mute === false) { + if (this.config.buttonOpts.mute.visible === false) { muteButton.style.display = "none"; unmuteButton.style.display = "none"; } - if (this.config.buttonOpts.saveState === false) saveState.style.display = "none"; - if (this.config.buttonOpts.loadState === false) loadState.style.display = "none"; - if (this.config.buttonOpts.saveSavFiles === false) saveSavFiles.style.display = "none"; - if (this.config.buttonOpts.loadSavFiles === false) loadSavFiles.style.display = "none"; - if (this.config.buttonOpts.gamepad === false) controlMenu.style.display = "none"; - if (this.config.buttonOpts.cheat === false) cheatMenu.style.display = "none"; - if (this.config.buttonOpts.cacheManager === false) cache.style.display = "none"; - if (this.config.buttonOpts.netplay === false) netplay.style.display = "none"; - if (this.config.buttonOpts.diskButton === false) diskButton[0].style.display = "none"; - if (this.config.buttonOpts.volumeSlider === false) volumeSlider.style.display = "none"; - if (this.config.buttonOpts.exitEmulation === false) exitEmulation.style.display = "none"; + if (this.config.buttonOpts.saveState.visible === false) saveState.style.display = "none"; + if (this.config.buttonOpts.loadState.visible === false) loadState.style.display = "none"; + if (this.config.buttonOpts.saveSavFiles.visible === false) saveSavFiles.style.display = "none"; + if (this.config.buttonOpts.loadSavFiles.visible === false) loadSavFiles.style.display = "none"; + if (this.config.buttonOpts.gamepad.visible === false) controlMenu.style.display = "none"; + if (this.config.buttonOpts.cheat.visible === false) cheatMenu.style.display = "none"; + if (this.config.buttonOpts.cacheManager.visible === false) cache.style.display = "none"; + if (this.config.buttonOpts.netplay.visible === false) netplay.style.display = "none"; + if (this.config.buttonOpts.diskButton.visible === false) diskButton[0].style.display = "none"; + if (this.config.buttonOpts.volumeSlider.visible === false) volumeSlider.style.display = "none"; + if (this.config.buttonOpts.contextMenu.visible === false) contextMenuButton.style.display = "none"; + if (this.config.buttonOpts.exitEmulation.visible === false) exitEmulation.style.display = "none"; } this.menu.failedToStart = () => { From 45cc488ebc630293821ef854fc7808fb3511c435 Mon Sep 17 00:00:00 2001 From: Allan Niles Date: Mon, 28 Apr 2025 22:24:19 -0600 Subject: [PATCH 018/106] Add update script (#990) * add update script * fix formatting * spacing * typo * spacing * new line --- .github/workflows/latest.yml | 2 +- README.md | 2 +- data/src/nipplejs.js | 2 +- data/src/socket.io.min.js | 6 +- data/version.json | 6 +- docs/contributors.json | 46 ++++++++ docs/{Contributors.md => contributors.md} | 101 +++++------------ package.json | 9 +- update.js | 126 ++++++++++++++++++++++ 9 files changed, 218 insertions(+), 82 deletions(-) create mode 100644 docs/contributors.json rename docs/{Contributors.md => contributors.md} (67%) create mode 100644 update.js diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index bcc8ba628..1de76fb68 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -16,7 +16,7 @@ jobs: run: | cd /mnt/HDD/public chmod -R 755 .EmulatorJS/ - - name: Update Stable + - name: Update Latest run: | cd /mnt/HDD/public/.EmulatorJS/ git fetch --all diff --git a/README.md b/README.md index 0b00d14d0..f51cf604e 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ If you want to help with localization, please check out the [localization](data/ [Configurator]: https://emulatorjs.org/editor -[Contributors]: docs/Contributors.md +[Contributors]: docs/contributors.md [Website]: https://emulatorjs.org/ [Usage]: https://emulatorjs.org/docs/ [Demo]: https://demo.emulatorjs.org/ diff --git a/data/src/nipplejs.js b/data/src/nipplejs.js index 48fa61c10..2c31fb623 100644 --- a/data/src/nipplejs.js +++ b/data/src/nipplejs.js @@ -1 +1 @@ -!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i():"function"==typeof define&&define.amd?define("nipplejs",[],i):"object"==typeof exports?exports.nipplejs=i():t.nipplejs=i()}(window,(function(){return function(t){var i={};function e(o){if(i[o])return i[o].exports;var n=i[o]={i:o,l:!1,exports:{}};return t[o].call(n.exports,n,n.exports,e),n.l=!0,n.exports}return e.m=t,e.c=i,e.d=function(t,i,o){e.o(t,i)||Object.defineProperty(t,i,{enumerable:!0,get:o})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,i){if(1&i&&(t=e(t)),8&i)return t;if(4&i&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(e.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&i&&"string"!=typeof t)for(var n in t)e.d(o,n,function(i){return t[i]}.bind(null,n));return o},e.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(i,"a",i),i},e.o=function(t,i){return Object.prototype.hasOwnProperty.call(t,i)},e.p="",e(e.s=0)}([function(t,i,e){"use strict";e.r(i);var o,n=function(t,i){var e=i.x-t.x,o=i.y-t.y;return Math.sqrt(e*e+o*o)},s=function(t){return t*(Math.PI/180)},r=function(t){return t*(180/Math.PI)},d=new Map,a=function(t){d.has(t)&&clearTimeout(d.get(t)),d.set(t,setTimeout(t,100))},p=function(t,i,e){for(var o,n=i.split(/[ ,]+/g),s=0;s=0&&this._handlers_[t].splice(this._handlers_[t].indexOf(i),1),this},_.prototype.trigger=function(t,i){var e,o=this,n=t.split(/[ ,]+/g);o._handlers_=o._handlers_||{};for(var s=0;ss&&n<3*s&&!t.lockX?i="up":n>-s&&n<=s&&!t.lockY?i="left":n>3*-s&&n<=-s&&!t.lockX?i="down":t.lockY||(i="right"),t.lockY||(e=n>-r&&n0?"up":"down"),t.force>this.options.threshold){var d,a={};for(d in this.direction)this.direction.hasOwnProperty(d)&&(a[d]=this.direction[d]);var p={};for(d in this.direction={x:e,y:o,angle:i},t.direction=this.direction,a)a[d]===this.direction[d]&&(p[d]=!0);if(p.x&&p.y&&p.angle)return t;p.x&&p.y||this.trigger("plain",t),p.x||this.trigger("plain:"+e,t),p.y||this.trigger("plain:"+o,t),p.angle||this.trigger("dir dir:"+i,t)}else this.resetDirection();return t};var P=k;function E(t,i){this.nipples=[],this.idles=[],this.actives=[],this.ids=[],this.pressureIntervals={},this.manager=t,this.id=E.id,E.id+=1,this.defaults={zone:document.body,multitouch:!1,maxNumberOfNipples:10,mode:"dynamic",position:{top:0,left:0},catchDistance:200,size:100,threshold:.1,color:"white",fadeTime:250,dataOnly:!1,restJoystick:!0,restOpacity:.5,lockX:!1,lockY:!1,shape:"circle",dynamicPage:!1,follow:!1},this.config(i),"static"!==this.options.mode&&"semi"!==this.options.mode||(this.options.multitouch=!1),this.options.multitouch||(this.options.maxNumberOfNipples=1);var e=getComputedStyle(this.options.zone.parentElement);return e&&"flex"===e.display&&(this.parentIsFlex=!0),this.updateBox(),this.prepareNipples(),this.bindings(),this.begin(),this.nipples}E.prototype=new T,E.constructor=E,E.id=0,E.prototype.prepareNipples=function(){var t=this.nipples;t.on=this.on.bind(this),t.off=this.off.bind(this),t.options=this.options,t.destroy=this.destroy.bind(this),t.ids=this.ids,t.id=this.id,t.processOnMove=this.processOnMove.bind(this),t.processOnEnd=this.processOnEnd.bind(this),t.get=function(i){if(void 0===i)return t[0];for(var e=0,o=t.length;e