Path: blob/main/public/webretro/assets/base.js
1224 views
// Source Code: https://github.com/BinBashBanana/webretro1// please dont use IE2var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;3if (!window.fetch || !indexedDB) {4alert("Update your browser!");5throw "Update your browser!";6}78var fsBundleDirs, fsBundleFiles, loadStatus, romName, isPaused, wasmReady, bundleReady, biosReady, romMode, core, wIdb, romUploadCallback, latestVersion, mainCompleted, currentManager, romUploadsReady, realRomExt, currentTheme;9var bundleCdn = "https://cdn.jsdelivr.net/gh/BinBashBanana/webretro@master/";10var bundleCdnLatest = "https://cdn.jsdelivr.net/gh/BinBashBanana/webretro/";11var biosCdn = "https://cdn.jsdelivr.net/gh/archtaurus/RetroPieBIOS@master/BIOS/";12var infoJsonUrl = "https://cdn.jsdelivr.net/gh/BinBashBanana/webretro/assets/info.json";13var standaloneDownloadUrl = "https://cdn.jsdelivr.net/gh/BinBashBanana/webretro/utils/webretro-standalone.html";14var relativeBase = (typeof relativeBase == "string") ? relativeBase : "";15var coreDir = "cores/";16var bioses = {"a5200": {path: "", files: ["5200.rom"]}, "atari800": {path: "", files: ["5200.rom"]}, "freechaf": {path: "", files: ["sl31253.bin", "sl31254.bin", "sl90025.bin"]}, "freeintv": {path: "", files: ["exec.bin", "grom.bin"]}, "gearcoleco": {path: "", files: ["colecovision.rom"]}, "handy": {path: "", files: ["lynxboot.img"]}, "mednafen_psx": {path: "", files: ["scph5500.bin", "scph5501.bin", "scph5502.bin"]}, "mednafen_psx_hw": {path: "", files: ["scph5500.bin", "scph5501.bin", "scph5502.bin"]}, "neocd": {path: "neocd/", files: ["neocd_z.rom"]}, "o2em": {path: "", files: ["c52.bin", "g7400.bin", "jopac.bin", "o2rom.bin"]}, "opera": {path: "", files: ["panafz10-norsa.bin", "panafz10ja-anvil-kanji.bin"]}, "px68k": {path: "keropi/", files: ["cgrom.dat", "iplrom.dat", "iplrom30.dat", "iplromco.dat", "iplromxv.dat"]}, "yabasanshiro": {path: "", files: ["saturn_bios.bin"]}, "yabause": {path: "", files: ["saturn_bios.bin"]}};17var defaultKeybinds = 'input_player1_start = "enter"\ninput_player1_select = "space"\ninput_player1_l = "e"\ninput_player1_l2 = "r"\ninput_player1_r = "p"\ninput_player1_r2 = "o"\ninput_player1_a = "h"\ninput_player1_b = "g"\ninput_player1_x = "y"\ninput_player1_y = "t"\ninput_player1_up = "up"\ninput_player1_left = "left"\ninput_player1_down = "down"\ninput_player1_right = "right"\ninput_player1_l_x_minus = "a"\ninput_player1_l_x_plus = "d"\ninput_player1_l_y_minus = "w"\ninput_player1_l_y_plus = "s"\ninput_player1_l3_btn = "x"\ninput_player1_r_x_minus = "j"\ninput_player1_r_x_plus = "l"\ninput_player1_r_y_minus = "i"\ninput_player1_r_y_plus = "k"\ninput_player1_r3_btn = "comma"\ninput_menu_toggle = "f1"\ninput_save_state = "f2"\ninput_load_state = "f3"\ninput_screenshot = "f4"\ninput_hold_fast_forward = "nul"\ninput_toggle_fast_forward = "nul"\ninput_hold_slowmotion = "nul"\ninput_toggle_slowmotion = "nul"\ninput_grab_mouse_toggle = "backslash"\ninput_game_focus_toggle = "tilde"\n';18var nulKeys = 'input_ai_service = "nul"\ninput_ai_service_axis = "nul"\ninput_ai_service_btn = "nul"\ninput_ai_service_mbtn = "nul"\ninput_audio_mute = "nul"\ninput_audio_mute_axis = "nul"\ninput_audio_mute_btn = "nul"\ninput_audio_mute_mbtn = "nul"\ninput_cheat_index_minus = "nul"\ninput_cheat_index_minus_axis = "nul"\ninput_cheat_index_minus_btn = "nul"\ninput_cheat_index_minus_mbtn = "nul"\ninput_cheat_index_plus = "nul"\ninput_cheat_index_plus_axis = "nul"\ninput_cheat_index_plus_btn = "nul"\ninput_cheat_index_plus_mbtn = "nul"\ninput_cheat_toggle = "nul"\ninput_cheat_toggle_axis = "nul"\ninput_cheat_toggle_btn = "nul"\ninput_cheat_toggle_mbtn = "nul"\ninput_desktop_menu_toggle = "nul"\ninput_desktop_menu_toggle_axis = "nul"\ninput_desktop_menu_toggle_btn = "nul"\ninput_desktop_menu_toggle_mbtn = "nul"\ninput_disk_eject_toggle = "nul"\ninput_disk_eject_toggle_axis = "nul"\ninput_disk_eject_toggle_btn = "nul"\ninput_disk_eject_toggle_mbtn = "nul"\ninput_disk_next = "nul"\ninput_disk_next_axis = "nul"\ninput_disk_next_btn = "nul"\ninput_disk_next_mbtn = "nul"\ninput_disk_prev = "nul"\ninput_disk_prev_axis = "nul"\ninput_disk_prev_btn = "nul"\ninput_disk_prev_mbtn = "nul"\ninput_duty_cycle = "nul"\ninput_enable_hotkey = "nul"\ninput_enable_hotkey_axis = "nul"\ninput_enable_hotkey_btn = "nul"\ninput_enable_hotkey_mbtn = "nul"\ninput_exit_emulator = "nul"\ninput_exit_emulator_axis = "nul"\ninput_exit_emulator_btn = "nul"\ninput_exit_emulator_mbtn = "nul"\ninput_fps_toggle = "nul"\ninput_fps_toggle_axis = "nul"\ninput_fps_toggle_btn = "nul"\ninput_fps_toggle_mbtn = "nul"\ninput_frame_advance = "nul"\ninput_frame_advance_axis = "nul"\ninput_frame_advance_btn = "nul"\ninput_frame_advance_mbtn = "nul"\ninput_game_focus_toggle_axis = "nul"\ninput_game_focus_toggle_btn = "nul"\ninput_game_focus_toggle_mbtn = "nul"\ninput_grab_mouse_toggle_axis = "nul"\ninput_grab_mouse_toggle_btn = "nul"\ninput_grab_mouse_toggle_mbtn = "nul"\ninput_hold_fast_forward_axis = "nul"\ninput_hold_fast_forward_btn = "nul"\ninput_hold_fast_forward_mbtn = "nul"\ninput_slowmotion = "nul"\ninput_hold_slowmotion_axis = "nul"\ninput_hold_slowmotion_btn = "nul"\ninput_hold_slowmotion_mbtn = "nul"\ninput_hotkey_block_delay = "nul"\ninput_load_state_axis = "nul"\ninput_load_state_btn = "nul"\ninput_load_state_mbtn = "nul"\ninput_menu_toggle_axis = "nul"\ninput_menu_toggle_btn = "nul"\ninput_menu_toggle_mbtn = "nul"\ninput_movie_record_toggle = "nul"\ninput_movie_record_toggle_axis = "nul"\ninput_movie_record_toggle_btn = "nul"\ninput_movie_record_toggle_mbtn = "nul"\ninput_netplay_game_watch = "nul"\ninput_netplay_game_watch_axis = "nul"\ninput_netplay_game_watch_btn = "nul"\ninput_netplay_game_watch_mbtn = "nul"\ninput_netplay_host_toggle = "nul"\ninput_netplay_host_toggle_axis = "nul"\ninput_netplay_host_toggle_btn = "nul"\ninput_netplay_host_toggle_mbtn = "nul"\ninput_osk_toggle = "nul"\ninput_osk_toggle_axis = "nul"\ninput_osk_toggle_btn = "nul"\ninput_osk_toggle_mbtn = "nul"\ninput_overlay_next = "nul"\ninput_overlay_next_axis = "nul"\ninput_overlay_next_btn = "nul"\ninput_overlay_next_mbtn = "nul"\ninput_pause_toggle = "nul"\ninput_pause_toggle_axis = "nul"\ninput_pause_toggle_btn = "nul"\ninput_pause_toggle_mbtn = "nul"\ninput_player1_a_axis = "nul"\ninput_player1_a_btn = "nul"\ninput_player1_a_mbtn = "nul"\ninput_player1_b_axis = "nul"\ninput_player1_b_btn = "nul"\ninput_player1_b_mbtn = "nul"\ninput_player1_down_axis = "nul"\ninput_player1_down_btn = "nul"\ninput_player1_down_mbtn = "nul"\ninput_player1_gun_aux_a = "nul"\ninput_player1_gun_aux_a_axis = "nul"\ninput_player1_gun_aux_a_btn = "nul"\ninput_player1_gun_aux_a_mbtn = "nul"\ninput_player1_gun_aux_b = "nul"\ninput_player1_gun_aux_b_axis = "nul"\ninput_player1_gun_aux_b_btn = "nul"\ninput_player1_gun_aux_b_mbtn = "nul"\ninput_player1_gun_aux_c = "nul"\ninput_player1_gun_aux_c_axis = "nul"\ninput_player1_gun_aux_c_btn = "nul"\ninput_player1_gun_aux_c_mbtn = "nul"\ninput_player1_gun_dpad_down = "nul"\ninput_player1_gun_dpad_down_axis = "nul"\ninput_player1_gun_dpad_down_btn = "nul"\ninput_player1_gun_dpad_down_mbtn = "nul"\ninput_player1_gun_dpad_left = "nul"\ninput_player1_gun_dpad_left_axis = "nul"\ninput_player1_gun_dpad_left_btn = "nul"\ninput_player1_gun_dpad_left_mbtn = "nul"\ninput_player1_gun_dpad_right = "nul"\ninput_player1_gun_dpad_right_axis = "nul"\ninput_player1_gun_dpad_right_btn = "nul"\ninput_player1_gun_dpad_right_mbtn = "nul"\ninput_player1_gun_dpad_up = "nul"\ninput_player1_gun_dpad_up_axis = "nul"\ninput_player1_gun_dpad_up_btn = "nul"\ninput_player1_gun_dpad_up_mbtn = "nul"\ninput_player1_gun_offscreen_shot = "nul"\ninput_player1_gun_offscreen_shot_axis = "nul"\ninput_player1_gun_offscreen_shot_btn = "nul"\ninput_player1_gun_offscreen_shot_mbtn = "nul"\ninput_player1_gun_select = "nul"\ninput_player1_gun_select_axis = "nul"\ninput_player1_gun_select_btn = "nul"\ninput_player1_gun_select_mbtn = "nul"\ninput_player1_gun_start = "nul"\ninput_player1_gun_start_axis = "nul"\ninput_player1_gun_start_btn = "nul"\ninput_player1_gun_start_mbtn = "nul"\ninput_player1_gun_trigger = "nul"\ninput_player1_gun_trigger_axis = "nul"\ninput_player1_gun_trigger_btn = "nul"\ninput_player1_gun_trigger_mbtn = "nul"\ninput_player1_l2_axis = "nul"\ninput_player1_l2_btn = "nul"\ninput_player1_l2_mbtn = "nul"\ninput_player1_l3 = "nul"\ninput_player1_l3_axis = "nul"\ninput_player1_l3_mbtn = "nul"\ninput_player1_l_axis = "nul"\ninput_player1_l_btn = "nul"\ninput_player1_l_mbtn = "nul"\ninput_player1_l_x_minus_axis = "nul"\ninput_player1_l_x_minus_btn = "nul"\ninput_player1_l_x_minus_mbtn = "nul"\ninput_player1_l_x_plus_axis = "nul"\ninput_player1_l_x_plus_btn = "nul"\ninput_player1_l_x_plus_mbtn = "nul"\ninput_player1_l_y_minus_axis = "nul"\ninput_player1_l_y_minus_btn = "nul"\ninput_player1_l_y_minus_mbtn = "nul"\ninput_player1_l_y_plus_axis = "nul"\ninput_player1_l_y_plus_btn = "nul"\ninput_player1_l_y_plus_mbtn = "nul"\ninput_player1_left_axis = "nul"\ninput_player1_left_mbtn = "nul"\ninput_player1_r2_axis = "nul"\ninput_player1_r2_btn = "nul"\ninput_player1_r2_mbtn = "nul"\ninput_player1_r3 = "nul"\ninput_player1_r3_axis = "nul"\ninput_player1_r3_mbtn = "nul"\ninput_player1_r_axis = "nul"\ninput_player1_r_btn = "nul"\ninput_player1_r_mbtn = "nul"\ninput_player1_r_x_minus_axis = "nul"\ninput_player1_r_x_minus_btn = "nul"\ninput_player1_r_x_minus_mbtn = "nul"\ninput_player1_r_x_plus_axis = "nul"\ninput_player1_r_x_plus_btn = "nul"\ninput_player1_r_x_plus_mbtn = "nul"\ninput_player1_r_y_minus_axis = "nul"\ninput_player1_r_y_minus_btn = "nul"\ninput_player1_r_y_minus_mbtn = "nul"\ninput_player1_r_y_plus_axis = "nul"\ninput_player1_r_y_plus_btn = "nul"\ninput_player1_r_y_plus_mbtn = "nul"\ninput_player1_right_axis = "nul"\ninput_player1_right_mbtn = "nul"\ninput_player1_select_axis = "nul"\ninput_player1_select_btn = "nul"\ninput_player1_select_mbtn = "nul"\ninput_player1_start_axis = "nul"\ninput_player1_start_btn = "nul"\ninput_player1_start_mbtn = "nul"\ninput_player1_turbo = "nul"\ninput_player1_turbo_axis = "nul"\ninput_player1_turbo_btn = "nul"\ninput_player1_turbo_mbtn = "nul"\ninput_player1_up_axis = "nul"\ninput_player1_up_btn = "nul"\ninput_player1_up_mbtn = "nul"\ninput_player1_x_axis = "nul"\ninput_player1_x_btn = "nul"\ninput_player1_x_mbtn = "nul"\ninput_player1_y_axis = "nul"\ninput_player1_y_btn = "nul"\ninput_player1_y_mbtn = "nul"\ninput_poll_type_behavior = "nul"\ninput_recording_toggle = "nul"\ninput_recording_toggle_axis = "nul"\ninput_recording_toggle_btn = "nul"\ninput_recording_toggle_mbtn = "nul"\ninput_reset = "nul"\ninput_reset_axis = "nul"\ninput_reset_btn = "nul"\ninput_reset_mbtn = "nul"\ninput_rewind = "nul"\ninput_rewind_axis = "nul"\ninput_rewind_btn = "nul"\ninput_rewind_mbtn = "nul"\ninput_save_state_axis = "nul"\ninput_save_state_btn = "nul"\ninput_save_state_mbtn = "nul"\ninput_screenshot_axis = "nul"\ninput_screenshot_btn = "nul"\ninput_screenshot_mbtn = "nul"\ninput_send_debug_info = "nul"\ninput_send_debug_info_axis = "nul"\ninput_send_debug_info_btn = "nul"\ninput_send_debug_info_mbtn = "nul"\ninput_shader_next = "nul"\ninput_shader_next_axis = "nul"\ninput_shader_next_btn = "nul"\ninput_shader_next_mbtn = "nul"\ninput_shader_prev = "nul"\ninput_shader_prev_axis = "nul"\ninput_shader_prev_btn = "nul"\ninput_shader_prev_mbtn = "nul"\ninput_state_slot_decrease = "nul"\ninput_state_slot_decrease_axis = "nul"\ninput_state_slot_decrease_btn = "nul"\ninput_state_slot_decrease_mbtn = "nul"\ninput_state_slot_increase = "nul"\ninput_state_slot_increase_axis = "nul"\ninput_state_slot_increase_btn = "nul"\ninput_state_slot_increase_mbtn = "nul"\ninput_streaming_toggle = "nul"\ninput_streaming_toggle_axis = "nul"\ninput_streaming_toggle_btn = "nul"\ninput_streaming_toggle_mbtn = "nul"\ninput_toggle_fast_forward_axis = "nul"\ninput_toggle_fast_forward_btn = "nul"\ninput_toggle_fast_forward_mbtn = "nul"\ninput_toggle_fullscreen = "nul"\ninput_toggle_fullscreen_axis = "nul"\ninput_toggle_fullscreen_btn = "nul"\ninput_toggle_fullscreen_mbtn = "nul"\ninput_toggle_slowmotion_axis = "nul"\ninput_toggle_slowmotion_btn = "nul"\ninput_toggle_slowmotion_mbtn = "nul"\ninput_turbo_default_button = "nul"\ninput_turbo_mode = "nul"\ninput_turbo_period = "nul"\ninput_volume_down = "nul"\ninput_volume_down_axis = "nul"\ninput_volume_down_btn = "nul"\ninput_volume_down_mbtn = "nul"\ninput_volume_up = "nul"\ninput_volume_up_axis = "nul"\ninput_volume_up_btn = "nul"\ninput_volume_up_mbtn = "nul"\n';19var extraConfig = 'rgui_show_start_screen = "false"\nnotification_show_remap_load = "false"\nmenu_mouse_enable = "true"\nmenu_pointer_enable = "true"\n';20var pdKeys = [8, 9, 13, 19, 27, 32, 33, 34, 35, 36, 42, 44, 45, 91, 92, 93, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135];21var webretroVersion = 6.5;22var maxConsoleLength = 10000;23var updateNotice = document.getElementById("updatenotice");24var versionIndicator = document.getElementById("versionindicator");25var webretroTitle = document.getElementById("webretrotitle");26var upload = document.getElementById("upload");27var googleDriveUpload = document.getElementById("googledriveupload");28var dropboxUpload = document.getElementById("dropboxupload");29var oneDriveUpload = document.getElementById("onedriveupload");30var startButton = document.getElementById("startbutton");31var smooth = document.getElementById("smooth");32var canvas = document.getElementById("canvas");33var canvasMask = document.getElementById("canvasmask");34var saveState = document.getElementById("savestate");35var loadState = document.getElementById("loadstate");36var undoSaveState = document.getElementById("undosavestate");37var undoLoadState = document.getElementById("undoloadstate");38var exportState = document.getElementById("exportstate");39var importState = document.getElementById("importstate");40var ffd = document.getElementById("ffd");41var ffdContent = document.getElementById("ffdcontent");42var coreSelectArea = document.getElementById("coreselectarea");43var uploadArea = document.getElementById("uploadarea");44var coreOrder = document.getElementById("coreorder");45var coreList = document.getElementById("corelist");46var systemName = document.getElementById("systemname");47var consoleButton = document.getElementById("consolebutton");48var resetButton = document.getElementById("resetbutton");49var resetButton2 = document.getElementById("resetbutton2");50var mouseGrabButton = document.getElementById("mousegrabbutton");51var gameFocusButton = document.getElementById("gamefocusbutton");52var fullscreenButton = document.getElementById("fullscreenbutton");53var downloadStandaloneButton = document.getElementById("downloadstandalonebutton");54var menuButton = document.getElementById("menubutton");55var pauseButton = document.getElementById("pause");56var resumeOverlay = document.getElementById("resume");57var sideAlertHolder = document.getElementById("sidealertholder");58var saveGame = document.getElementById("savegame");59var exportSave = document.getElementById("exportsave");60var importSave = document.getElementById("importsave");61var autosave = document.getElementById("autosave");62var mainArea = document.getElementById("mainarea");63var menuBar = document.getElementById("menubar");64var menuHider = document.getElementById("menuhider");65var menuHeight = 45;66var actualMenuHeight = menuHeight;67var canvasCssWorkaroundElement = document.createElement("style");68var themeSelector = document.getElementById("themeselector");69var themes = {"iodinelight": {menuHeight: 45, id: ""}, "iodinedark": {menuHeight: 45, id: "iodinedark"}, "webplayer": {menuHeight: 65, id: "webplayer"}, "webplayernavy": {menuHeight: 65, id: "webplayer navy"}};70var defaultTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "iodinedark" : "iodinelight";71var loadingDiv = document.getElementById("loadingdiv");72var loadingText = document.getElementById("loadingtext");73var loadingBar = document.getElementById("loadingbar");74var takeScreenshot = document.getElementById("takescreenshot");75var modals = document.getElementById("modals");76var keybindTable = document.getElementById("keybindtable");77var saveKeybinds = document.getElementById("savekeybinds");78var resetKeybinds = document.getElementById("resetkeybinds");79var keybindsButton = document.getElementById("keybindsbutton");80var screenshotsButton = document.getElementById("screenshotsbutton");81var savesButton = document.getElementById("savesbutton");82var statesButton = document.getElementById("statesbutton");83var downloadAllScreenshots = document.getElementById("downloadallscreenshots");84var screenshotsDiv = document.getElementById("screenshotsdiv");85var saveTable = document.getElementById("savetable");86var romSelectorTable = document.getElementById("romselectortable");87var pso = document.getElementById("pso");88var coreOptions = {"a5200": {"lowpass": 'a5200_low_pass_filter = "enabled"\n'}, "mednafen_psx": {"highres": 'beetle_psx_internal_resolution = "2x"\n', "ditherscaling": 'beetle_psx_dither_mode = "internal resolution"\n', "widescreen": 'beetle_psx_widescreen_hack = "enabled"\n', "antijitter": 'beetle_psx_pgxp_mode = "memory only"\n'}, "mednafen_psx_hw": {"softwarerenderer": 'beetle_psx_hw_renderer = "software"\n', "highres": 'beetle_psx_hw_internal_resolution = "2x"\n', "ditherscaling": 'beetle_psx_hw_dither_mode = "internal resolution"\n', "widescreen": 'beetle_psx_hw_widescreen_hack = "enabled"\n', "antijitter": 'beetle_psx_hw_pgxp_mode = "memory only"\n'}, "mednafen_vb": {"anaglyph": 'vb_anaglyph_preset = "red & blue"\n'}, "mednafen_wswan": {"75hz": 'wswan_60hz_mode = "disabled"\n', "portrait": 'wswan_rotate_display = "portrait"\n', "lowpass": 'wswan_sound_low_pass = "enabled"\n'}, "melonds": {"leftright": 'melonds_screen_layout = "Left/Right"\n'}, "mgba": {"lowpass": 'mgba_audio_low_pass_filter = "enabled"\n'}, "mupen64plus_next": {"highres": 'mupen64plus-169screensize = "1280x720"\nmupen64plus-43screensize = "960x720"\n', "widescreen": 'mupen64plus-aspect = "16:9 adjusted"\n'}, "o2em": {"lowpass": 'o2em_low_pass_filter = "enabled"\n'}, "parallel_n64": {"highres": 'parallel-n64-screensize = "960x720"\n', "renderer2": 'parallel-n64-gfxplugin = "gln64"\n'}, "prosystem": {"lowpass": 'prosystem_low_pass_filter = "enabled"\n'}, "snes9x": {"mouse": 'input_libretro_device_p1 = "2"\n'}, "stella2014": {"lowpass": 'stella2014_low_pass_filter = "enabled"\n'}, "vecx": {"softwarerenderer": 'vecx_use_hw = "Software"\n'}, "virtualjaguar": {"fastblitter": 'virtualjaguar_usefastblitter = "enabled"\n'}, "yabause": {"frameskip": 'yabause_frameskip = "enabled"\n'}};89var managers = {};90managers.keybind = document.getElementById("keybindmanager");91managers.screenshot = document.getElementById("screenshotmanager");92managers.save = document.getElementById("savemanager");93managers.romSelector = document.getElementById("romselector");94var managerNames = {"save": "Saves & States", "romSelector": "Select the master ROM or playlist"};95var managerTitle = document.getElementById("managertitle");96var managerClose = document.getElementById("managerclose");97var screenshotDatas = [];98var screenshotObjUrls = [];99var saveIDs = [];100var quotaText = document.getElementById("quotatext");101var recommendedExtensions = document.getElementById("recommendedextensions");102var systems = {"81": "ZX81", "dosbox": "MS-DOS", "dosbox_pure": "MS-DOS", "opera": "3DO", "fsuae": "Amiga", "puae": "Amiga", "cap32": "Amstrad CPC", "fbalpha2012": "Arcade", "fbneo": "Arcade", "mame2003_plus": "Arcade", "stella": "Atari 2600", "stella2014": "Atari 2600", "atari800": "Atari 5200", "a5200": "Atari 5200", "prosystem": "Atari 7800", "virtualjaguar": "Atari Jaguar", "handy": "Atari Lynx", "mednafen_lynx": "Atari Lynx", "hatari": "Atari ST/TT", "gearcoleco": "ColecoVision", "vice_x64": "Commodore 64", "bk": "Electronika BK", "freechaf": "Fairchild Channel F", "gw": "Game & Watch", "vba_next": "GBA", "vbam": "GBA", "meteor": "GBA", "mednafen_gba": "GBA", "gpsp": "GBA", "mgba": "GB/GBC/GBA", "gambatte": "GB/GBC", "gearboy": "GB/GBC", "tgbdual": "GB/GBC", "sameboy": "GB/GBC", "o2em": "Odyssey 2", "freeintv": "Intellivision", "fmsx": "MSX", "bluemsx": "MSX", "neocd": "Neo-Geo CD", "mednafen_ngp": "Neo-Geo Pocket", "bnes": "NES", "fceumm": "NES", "quicknes": "NES", "mesen": "NES", "nestopia": "NES", "citra": "Nintendo 3DS", "mupen64plus_next": "Nintendo 64", "parallel_n64": "Nintendo 64", "desmume2015": "Nintendo DS", "desmume": "Nintendo DS", "melonds": "Nintendo DS", "dolphin": "Nintendo GC/Wii", "mednafen_pce_fast": "PC Engine", "mednafen_supergrafx": "PC Engine SuperGrafx", "quasi88": "PC-8000/8800", "np2kai": "PC-98", "mednafen_pcfx": "PC-FX", "mednafen_psx": "PlayStation", "mednafen_psx_hw": "PlayStation", "pcsx2": "PlayStation 2", "ppsspp": "PSP", "scummvm": "ScummVM", "flycast": "Sega Dreamcast", "mednafen_saturn": "Sega Saturn", "mednafen_saturn_hw": "Sega Saturn", "yabause": "Sega Saturn", "yabasanshiro": "Sega Saturn", "kronos": "Sega Saturn", "picodrive": "Sega Systems", "genesis_plus_gx": "Sega Systems", "blastem": "Sega Genesis", "px68k": "Sharp X68000", "fuse": "ZX Spectrum", "bsnes": "SNES", "bsnes_mercury_performance": "SNES", "bsnes_mercury_balanced": "SNES", "bsnes_mercury_accuracy": "SNES", "mednafen_snes": "SNES", "mesen-s": "SNES", "snes9x": "SNES", "theodore": "Thomson MO/TO", "vecx": "Vectrex", "mednafen_vb": "Virtual Boy", "mednafen_wswan": "WonderSwan"};103var coreNames = {"81": "EightyOne", "dosbox": "DOSBox", "dosbox_pure": "DOSBox Pure", "opera": "Opera", "fsuae": "FS-UAE", "puae": "PUAE", "cap32": "Caprice32", "fbalpha2012": "FB Alpha 2012", "fbneo": "FB Neo", "mame2003_plus": "MAME 2003 Plus", "stella": "Stella", "stella2014": "Stella 2014", "atari800": "Atari800", "a5200": "a5200", "prosystem": "ProSystem", "virtualjaguar": "Virtual Jaguar", "handy": "Handy", "mednafen_lynx": "Beetle Lynx", "hatari": "Hatari", "gearcoleco": "Gearcoleco", "vice_x64": "VICE", "bk": "BK", "freechaf": "FreeChaF", "gw": "GW", "vba_next": "VBA-Next", "vbam": "VBA-M", "meteor": "Meteor", "mednafen_gba": "Beetle GBA", "gpsp": "gpSP", "mgba": "mGBA", "gambatte": "Gambatte", "gearboy": "Gearboy", "tgbdual": "TGB Dual", "sameboy": "SameBoy", "o2em": "O2EM", "freeintv": "FreeIntv", "fmsx": "fMSX", "bluemsx": "blueMSX", "neocd": "NeoCD", "mednafen_ngp": "Beetle NeoPop", "bnes": "bnes", "fceumm": "FCEUmm", "quicknes": "QuickNES", "mesen": "Mesen", "nestopia": "Nestopia UE", "citra": "Citra", "mupen64plus_next": "Mupen64Plus-Next", "parallel_n64": "ParaLLEl N64", "desmume2015": "DeSmuME 2015", "desmume": "DeSmuME", "melonds": "melonDS", "dolphin": "Dolphin", "mednafen_pce_fast": "Beetle PCE Fast", "mednafen_supergrafx": "Beetle SuperGrafx", "quasi88": "QUASI88", "np2kai": "NP2kai", "mednafen_pcfx": "Beetle PCFX", "mednafen_psx": "Beetle PSX", "mednafen_psx_hw": "Beetle PSX HW", "pcsx2": "PCSX2", "ppsspp": "PPSSPP", "scummvm": "ScummVM", "flycast": "Flycast", "mednafen_saturn": "Beetle Saturn", "mednafen_saturn_hw": "Beetle Saturn HW", "yabause": "Yabause", "yabasanshiro": "YabaSanshiro", "kronos": "Kronos", "picodrive": "PicoDrive", "genesis_plus_gx": "Genesis Plus GX", "blastem": "BlastEm", "px68k": "px68k", "fuse": "Fuse", "bsnes": "bsnes", "bsnes_mercury_performance": "bsnes-mercury Performance", "bsnes_mercury_balanced": "bsnes-mercury Balanced", "bsnes_mercury_accuracy": "bsnes-mercury Accuracy", "mednafen_snes": "Beetle SNES", "mesen-s": "Mesen-S", "snes9x": "Snes9x", "theodore": "Theodore", "vecx": "Vecx", "mednafen_vb": "Beetle VB", "mednafen_wswan": "Beetle WonderSwan"};104var fileExts = {"3DO": "", "Amiga": ".adf, .adz, .dms, .ipf, .hdf, .hdz", "Amstrad CPC": ".dsk, .sna, .cdt, .voc, .cpr", "Arcade": "", "Atari 2600": ".a26", "Atari 5200": ".a52", "Atari 7800": ".a78", "Atari Jaguar": ".j64, .jag, .abs, .cof, .prg", "Atari Lynx": ".lnx", "Atari ST/TT": ".st, .stx, .msa", "ColecoVision": ".col, .cv", "Commodore 64": ".d64, .d6z, .d71, .d7z, .d80, .d81, .d82, .d8z, .g64, .g6z, .g41, .g4z, .x64, .x6z, .nib, .nbz, .d2m, .d4m, .t64, .p00", "Electronika BK": "", "Fairchild Channel F": ".chf", "GB/GBC/GBA": ".gb, .gbc, .gba", "GBA": ".gba", "Game & Watch": ".mgw", "GB/GBC": ".gb, .gbc", "Intellivision": ".int", "MS-DOS": ".exe", "MSX": ".mx1, .mx2, .dsk, .cas", "NES": ".nes, .fds, .unf, .unif", "Neo-Geo CD": "", "Neo-Geo Pocket": ".ngp, .ngc", "Nintendo 3DS": ".3ds, .3dsx, .cci, .cxi", "Nintendo 64": ".n64, .v64, .z64, .u1, .ndd", "Nintendo DS": ".nds, .srl", "Nintendo GC/Wii": ".gcm, .dol, .tgc, .wbfs, .gcz, .wad", "Odyssey 2": "", "PC Engine SuperGrafx": ".sgx", "PC Engine": ".pce", "PC-8000/8800": ".d88, .88d, .u88, .88u", "PC-98": ".d98, .98d, .fdi, .fdd, .tfd, .hdm, .hdi", "PC-FX": "", "PSP": ".pbp", "PlayStation 2": "", "PlayStation": "", "SNES": ".smc, .sfc, .swc, .fig, .bs", "ScummVM": ".exe", "Sega Dreamcast": ".cdi, .gdi, .lst", "Sega Systems": ".mdx, .md, .smd, .gen, .sms, .gg, .sg, .68k, .sgd", "Sega Genesis": ".mdx, .md, .smd, .gen, .sms, .68k, .sgd", "Sega Saturn": "", "Sharp X68000": ".dim, .dup, .2hd, .xdf, .hdf", "ZX Spectrum": ".tzx, .tap, .z80, .rzx, .scl", "ZX81": ".p, .t81", "Thomson MO/TO": ".fd, .sap, .k7, .m7, .m5", "Vectrex": ".vec", "Virtual Boy": ".vb, .vboy", "WonderSwan": ".ws, .wsc, .pc2"};105var multiFileCores = ["dosbox", "dosbox_pure", "opera", "fsuae", "puae", "cap32", "fbalpha2012", "fbneo", "mame2003_plus", "vice_x64", "neocd", "mednafen_supergrafx", "mednafen_pce_fast", "quasi88", "mednafen_pcfx", "mednafen_psx", "mednafen_psx_hw", "scummvm", "flycast", "mednafen_saturn", "mednafen_saturn_hw", "yabause", "yabasanshiro", "kronos", "px68k"];106var exclusiveMultiFileCores = ["dosbox", "dosbox_pure", "fbalpha2012", "fbneo", "mame2003_plus", "scummvm"]; // used for arcade systems, etc107var playlistExts = ".m3u, .cue, .ccd";108var playlistCores = ["opera", "fsuae", "puae", "cap32", "vice_x64", "neocd", "mednafen_supergrafx", "mednafen_pce_fast", "quasi88", "mednafen_pcfx", "mednafen_psx", "mednafen_psx_hw", "flycast", "mednafen_saturn", "mednafen_saturn_hw", "yabause", "yabasanshiro", "kronos", "px68k"]; // all of these must also be in multiFileCores109var cdromExts = ".iso, .img, .ciso, .cso, .chd";110var cdromCores = ["opera", "fsuae", "puae", "neocd", "dolphin", "mednafen_supergrafx", "mednafen_pce_fast", "ppsspp", "pcsx2", "mednafen_psx", "mednafen_psx_hw", "mednafen_saturn", "mednafen_saturn_hw", "yabause", "yabasanshiro", "kronos"]; // probably put these in playlistCores too111var multiSaveCores = ["pcsx2", "mednafen_psx", "mednafen_psx_hw", "scummvm", "flycast", "mednafen_saturn", "mednafen_saturn_hw"];112var noSaveCores = ["81", "dosbox", "dosbox_pure", "stella", "stella2014", "atari800", "a5200", "prosystem", "handy", "mednafen_lynx", "hatari", "bk", "freechaf", "gw", "freeintv", "bluemsx", "fmsx", "o2em", "np2kai", "px68k", "fuse"];113var noStateCores = ["dosbox", "atari800", "virtualjaguar", "hatari", "bk", "gw", "bluemsx", "pcsx2", "scummvm", "px68k"];114var preferredCores = ["opera", "puae", "cap32", "fbneo", "stella2014", "a5200", "prosystem", "virtualjaguar", "handy", "hatari", "gearcoleco", "vice_x64", "bk", "freechaf", "mgba", "vba_next", "gw", "sameboy", "freeintv", "dosbox_pure", "fmsx", "nestopia", "neocd", "mednafen_ngp", "citra", "mupen64plus_next", "melonds", "dolphin", "o2em", "mednafen_pce_fast", "mednafen_supergrafx", "np2kai", "quasi88", "mednafen_pcfx", "ppsspp", "mednafen_psx_hw", "pcsx2", "snes9x", "scummvm", "flycast", "blastem", "kronos", "genesis_plus_gx", "px68k", "81", "fuse", "theodore", "vecx", "mednafen_vb", "mednafen_wswan"];115var allCores = Object.keys(systems);116var allSystems = Object.keys(fileExts);117var allFileExts = Array.from(new Set(Object.values(fileExts).filter(i => i).join(", ").split(", "))).join(", ");118var systemsExperimentalFormat = Object.fromEntries(Object.values(systems).map(i => [i, allCores.filter(j => systems[j] == i)]));119var installedCores = ["a5200", "freechaf", "freeintv", "gearcoleco", "genesis_plus_gx", "handy", "mednafen_ngp", "mednafen_psx_hw", "mednafen_vb", "mednafen_wswan", "melonds", "mgba", "mupen64plus_next", "neocd", "nestopia", "o2em", "opera", "parallel_n64", "prosystem", "snes9x", "stella2014", "vecx", "virtualjaguar", "yabause"];120var installedSystems = allSystems.filter(i => installedCores.some(j => allCores.filter(k => systems[k] == i).includes(j)));121var installedFileExts = installedSystems.map(i => fileExts[i]).filter(i => i).join(", ");122var coreGithub = document.getElementById("coregithub");123var coreGithubLinks = {"81": "libretro/81-libretro", "dosbox": "libretro/dosbox-libretro", "dosbox_pure": "schellingb/dosbox-pure", "opera": "libretro/opera-libretro", "fsuae": "libretro/libretro-fsuae", "puae": "libretro/libretro-uae", "cap32": "libretro/libretro-cap32", "fbalpha2012": "libretro/fbalpha2012", "fbneo": "libretro/FBNeo", "mame2003_plus": "libretro/mame2003-plus-libretro", "stella": "stella-emu/stella", "stella2014": "libretro/stella2014-libretro", "atari800": "libretro/libretro-atari800", "a5200": "libretro/a5200", "prosystem": "libretro/prosystem-libretro", "virtualjaguar": "libretro/virtualjaguar-libretro", "handy": "libretro/libretro-handy", "mednafen_lynx": "libretro/beetle-lynx-libretro", "hatari": "libretro/hatari", "gearcoleco": "drhelius/Gearcoleco", "vice_x64": "libretro/vice-libretro", "bk": "libretro/bk-emulator", "freechaf": "libretro/FreeChaF", "gw": "libretro/gw-libretro", "vba_next": "libretro/vba-next", "vbam": "libretro/vbam-libretro", "meteor": "libretro/meteor-libretro", "mednafen_gba": "libretro/beetle-gba-libretro", "gpsp": "libretro/gpsp", "mgba": "libretro/mgba", "gambatte": "libretro/gambatte-libretro", "gearboy": "drhelius/Gearboy", "tgbdual": "libretro/tgbdual-libretro", "sameboy": "libretro/SameBoy", "o2em": "libretro/libretro-o2em", "freeintv": "libretro/FreeIntv", "fmsx": "libretro/fmsx-libretro", "bluemsx": "libretro/blueMSX-libretro", "neocd": "libretro/neocd_libretro", "mednafen_ngp": "libretro/beetle-ngp-libretro", "bnes": "libretro/bnes-libretro", "fceumm": "libretro/libretro-fceumm", "quicknes": "libretro/QuickNES_Core", "mesen": "libretro/Mesen", "nestopia": "libretro/nestopia", "citra": "libretro/citra", "mupen64plus_next": "libretro/mupen64plus-libretro-nx", "parallel_n64": "libretro/parallel-n64", "desmume2015": "libretro/desmume2015", "desmume": "libretro/desmume", "melonds": "libretro/melonds", "dolphin": "libretro/dolphin", "mednafen_pce_fast": "libretro/beetle-pce-fast-libretro", "mednafen_supergrafx": "libretro/beetle-supergrafx-libretro", "quasi88": "libretro/quasi88-libretro", "np2kai": "AZO234/NP2kai", "mednafen_pcfx": "libretro/beetle-pcfx-libretro", "mednafen_psx": "libretro/beetle-psx-libretro", "mednafen_psx_hw": "libretro/beetle-psx-libretro", "pcsx2": "libretro/pcsx2", "ppsspp": "hrydgard/ppsspp", "scummvm": "libretro/scummvm", "flycast": "flyinghead/flycast", "mednafen_saturn": "libretro/beetle-saturn-libretro", "mednafen_saturn_hw": "libretro/beetle-saturn-libretro", "yabause": "libretro/yabause", "yabasanshiro": "libretro/yabause/tree/yabasanshiro", "kronos": "libretro/yabause/tree/kronos", "picodrive": "libretro/picodrive", "genesis_plus_gx": "libretro/Genesis-Plus-GX", "blastem": "libretro/blastem", "px68k": "libretro/px68k-libretro", "fuse": "libretro/fuse-libretro", "bsnes": "libretro/bsnes-libretro", "bsnes_mercury_performance": "libretro/bsnes-mercury", "bsnes_mercury_balanced": "libretro/bsnes-mercury", "bsnes_mercury_accuracy": "libretro/bsnes-mercury", "mednafen_snes": "libretro/beetle-bsnes-libretro", "mesen-s": "libretro/Mesen-S", "snes9x": "libretro/snes9x", "theodore": "Zlika/theodore", "vecx": "libretro/libretro-vecx", "mednafen_vb": "libretro/beetle-vb-libretro", "mednafen_wswan": "libretro/beetle-wswan-libretro"};124var baseFsBundleDir = "/home/web_user/retroarch/bundle";125var baseFsSystemDir = "/home/web_user/retroarch/userdata/system/";126var baseFsConfigDir = "/home/web_user/retroarch/userdata/config/";127var baseFsSaveDir = "/home/web_user/retroarch/userdata/saves/";128var FSTracking = new EventTarget();129var writeToFileCooldown = {};130var saveObj = {};131var bundleErrors = 0;132var sramExts = ".srm, .sram, .ram, .gam, .sav, .dsv, .nvr, .SNA, .mcr";133var smasBrickFix = {"16a160ddd431a3db6fcd7453ffae9c4c": [80,65,84,67,72,0,127,160,0,8,169,1,133,160,141,0,22,107,1,191,182,0,4,34,160,255,0,6,189,164,0,4,34,160,255,0,69,79,70], "e87d43969bdf563d1148e3b35e8b5360": [80,65,84,67,72,0,129,160,0,8,169,1,133,160,141,0,22,107,1,193,182,0,4,34,160,255,0,6,191,164,0,4,34,160,255,0,69,79,70], "2071b049a463cefd7a0b7aeab8037ca0": [80,65,84,67,72,0,127,160,0,8,169,1,133,160,141,0,22,107,1,191,190,0,4,34,160,255,0,6,189,164,0,4,34,160,255,0,69,79,70]}; // Couldn't find SMAS+W SMC ROM [80,65,84,67,72,0,129,160,0,8,169,1,133,160,141,0,22,107,1,193,190,0,4,34,160,255,0,6,191,164,0,4,34,160,255,0,69,79,70]134// disable webcam for gameboy camera135var disableWebCam = true;136var appIsPwa = window.matchMedia("(display-mode: standalone)").matches;137// https://stackoverflow.com/a/11381730138var appIsPhone = false;139var appIsTouchscreen = false;140try {141(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) appIsPhone = true;})(navigator.userAgent||navigator.vendor||window.opera);142(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) appIsTouchscreen = true;})(navigator.userAgent||navigator.vendor||window.opera);143} catch (e) {144console.warn(e);145}146147// query string into object148var queries = Object.fromEntries(window.location.search.substring(1).split("&").map(i => i.split("=")).map(i => i.map(i => i && decodeURIComponent(i))));149150// core lists151function sortArray(array, sortTo) {152var orig = array.map(i => sortTo[i].toLowerCase());153var sorted = orig.map(i => i).sort();154var reSort = [];155var prev = -1;156var findex = -1;157for (var i = 0; i < sorted.length; i++) {158var curr = orig.indexOf(sorted[i]);159if (curr == findex) { // hit, find the next one160curr = orig.indexOf(sorted[i], prev + 1);161} else { // miss, set new findex162findex = curr;163}164prev = curr;165reSort.push(array[curr]);166}167return reSort;168}169170function updateCoreList() {171var coreArr;172switch (coreOrder.selectedIndex) {173case 0:174coreArr = sortArray(installedCores, systems);175break;176case 1:177coreArr = sortArray(installedCores, coreNames);178break;179case 2:180coreArr = installedCores;181break;182default:183coreArr = installedCores;184break;185}186187var aCoreList = "";188for (var i = 0; i < coreArr.length; i++) {189aCoreList += '<li><a href="?core=' + coreArr[i] + '">' + (coreNames[coreArr[i]] || coreArr[i]) + ' (' + systems[coreArr[i]] + ')</a></li>';190}191coreList.innerHTML = aCoreList;192}193194function showCoreList() {195updateCoreList();196document.body.classList.add("coreselect");197coreSelectArea.style.display = "block";198uploadArea.style.display = "none";199ffd.style.display = "block";200}201202coreOrder.onchange = function() {203updateCoreList();204}205206// back-forward cache fix. this was the only way that I found to do this. ULTRA STUPID!!!!!207window.addEventListener("load", function() {208window.setTimeout(function() {209updateCoreList();210}, 0);211}, false);212213// Binary to UTF-8214function u8atoutf8(data) {215return new TextDecoder().decode(data);216}217218function avShift(array, shift) {219for (var i = 0; i < array.length; i++) {220array[i] += shift;221}222return array;223}224225// date time226function getTime() {227var dateTime = new Date();228return dateTime.getFullYear().toString()+"-"+(dateTime.getMonth()+1).toString()+"-"+dateTime.getDate().toString()+"-"+dateTime.getHours().toString()+"-"+dateTime.getMinutes().toString();229}230231// bytes to human-readable string232function bytesToHumanReadable(bytes, si) {233bytes = bytes || 0;234var extension = -1;235while (bytes >= 1000) {236bytes /= si ? 1000 : 1024;237extension++;238}239return extension >= 8 ? "overflow" : bytes.toFixed(2) + " " + "KMGTPEZY".charAt(extension) + (!si && (extension > -1) ? "i" : "") + "B";240}241242// js has no built-in capitalization function243function capitalize(str) {244return str.charAt(0).toUpperCase() + str.slice(1);245}246247// key press stuff248function fakeKey(type, info) {249var e = new KeyboardEvent(type, {code: info.code || undefined, key: info.key || undefined, shiftKey: info.shiftKey || undefined});250document.dispatchEvent(e);251}252253function fakeKeyPress(info) {254fakeKey("keydown", info);255window.setTimeout(function() {256fakeKey("keyup", info);257}, 50);258}259260function fakeCharPress(key) {261if (charToCodeMap.hasOwnProperty(key)) fakeKeyPress({code: charToCodeMap[key].code, key: charToKeyMap.hasOwnProperty(key) ? charToKeyMap[key].key : key, shiftKey: charToCodeMap[key].hasOwnProperty("shift") ? true : false});262}263264function sendText(text) {265for (var i = 0; i < text.length; i++) {266fakeCharPress(text.charAt(i));267}268}269270function configIDToCode(configid) {271return Object.keys(codeToConfigIDMap).find(k => codeToConfigIDMap[k] == configid);272}273274function isAudioAllowed() {275try {276var audio = new AudioContext();277var state = audio.state;278audio.close();279return (state == "running");280} catch (e) {281return false;282}283}284285// indexedDB286function setIdbItem(key, value, customTransaction) {287(customTransaction || wIdb.transaction("main", "readwrite")).objectStore("main").put({key: key, value: value});288}289290function getIdbItem(key, customTransaction) {291return new Promise(function(resolve) {292(customTransaction || wIdb.transaction("main", "readwrite")).objectStore("main").get(key).onsuccess = function(e) {293resolve(e.target.result ? e.target.result.value : null);294}295});296}297298function getAllIdbItems(customTransaction) {299return new Promise(function(resolve) {300(customTransaction || wIdb.transaction("main", "readwrite")).objectStore("main").getAll().onsuccess = function(e) {301resolve(e.target.result ? e.target.result : null);302}303});304}305306function removeIdbItem(key, customTransaction) {307(customTransaction || wIdb.transaction("main", "readwrite")).objectStore("main").delete(key);308}309310function openIdb() {311var request = indexedDB.open("webretro", 2);312request.onsuccess = function(e) {313wIdb = e.target.result;314}315request.onupgradeneeded = function(e) {316wIdb = e.target.result;317var transaction = e.target.transaction;318319switch (e.oldVersion) {320case 0:321// create the object store322wIdb.createObjectStore("main", {keyPath: "key"}).transaction.oncomplete = function() {323// look for saves in localStorage from old versions324var ls = Object.keys(localStorage);325for (var i = 0; i < ls.length; i++) {326if (ls[i].startsWith("RetroArch_saves_")) {327setIdbItem(ls[i], [{ext: ".srm", dir: "", data: new Uint8Array(JSON.parse(localStorage.getItem(ls[i])))}], transaction);328localStorage.removeItem(ls[i]);329}330}331}332break;333case 1:334// move the saves into arrays335(async function() {336var allItems = await getAllIdbItems(transaction);337for (var i = 0; i < allItems.length; i++) {338if (allItems[i].key.startsWith("RetroArch_saves_")) {339setIdbItem(allItems[i].key, [{ext: ".srm", dir: "", data: allItems[i].value}], transaction);340}341}342})();343break;344}345}346}347348openIdb();349350// side alerts351function sideAlert(initialText, time) {352var p = document.createElement("p");353p.className = "sidealert";354p.appendChild(document.createTextNode(initialText));355sideAlertHolder.appendChild(p);356window.setTimeout(function() {357p.classList.add("on");358}, 10);359this.dismiss = function() {360p.classList.remove("on");361window.setTimeout(function() {362p.remove();363}, 100);364}365this.setText = function(text) {366p.textContent = text;367}368if (time) window.setTimeout(this.dismiss, time);369}370371// change background for status messages372function setStatus(message) {373loadStatus = message;374loadingText.textContent = message;375}376377// remove status messages378function removeStatus(message) {379if (loadStatus == message) setStatus("");380}381382// adjust canvas size to window383function adjustCanvasSize() {384var dpi = window.devicePixelRatio || 1;385var width = window.innerWidth * dpi;386var height = (window.innerHeight * dpi) - (actualMenuHeight * dpi);387if (Module && Module.setCanvasSize) {388Module.setCanvasSize(width, height);389} else {390canvas.width = width;391canvas.height = height;392}393}394window.addEventListener("resize", adjustCanvasSize, false);395adjustCanvasSize();396397// emscripten is stupid and removes css width and height properties from the canvas - https://github.com/emscripten-core/emscripten/issues/6353398function canvasCssWorkaround(css) {399canvasCssWorkaroundElement.textContent = "#canvas { " + css + " }";400document.head.appendChild(canvasCssWorkaroundElement);401}402403// adjust the menu bar height404function adjustActualMenuHeight() {405canvasCssWorkaround("top: " + actualMenuHeight + "px; height: calc(100vh - " + actualMenuHeight + "px);");406canvasMask.style.top = "" + actualMenuHeight + "px";407canvasMask.style.height = "calc(100vh - " + actualMenuHeight + "px)";408409menuBar.style.height = "" + actualMenuHeight + "px";410411adjustCanvasSize();412}413414// menu hider415function adjustMenuHeight() {416if (menuHider.checked) {417actualMenuHeight = 0;418adjustActualMenuHeight();419} else {420actualMenuHeight = menuHeight;421adjustActualMenuHeight();422}423}424menuHider.onchange = adjustMenuHeight;425426// logging427function log(log, userInput) {428console.log(log);429if (maxConsoleLength > 0 && wconsole.textContent.length > maxConsoleLength) wconsole.textContent = wconsole.textContent.substring(wconsole.textContent.indexOf("\n") + 1);430wconsole.textContent += (userInput ? "> " + userInput + "\n\t" + JSON.stringify(log) : log) + "\n";431wconsole.scrollTo({top: wconsole.scrollHeight});432}433434// xhr435function grab(url, type, success, fail) {436var req = new XMLHttpRequest();437req.open("GET", url, true);438req.overrideMimeType("text/plain; charset=x-user-defined");439req.responseType = type;440req.timeout = 8000;441req.onload = function() {442if (req.status >= 400) {443if (fail) fail(req.status);444} else {445if (success) success(this.response);446}447}448req.onerror = function() {449if (fail) fail(0);450}451req.ontimeout = function() {452if (fail) fail(0);453}454req.send();455}456457// file readers458function readFile(file) {459return new Promise(function(resolve) {460var reader = new FileReader();461reader.onload = function() {462resolve(this.result);463}464reader.readAsArrayBuffer(file);465});466}467468function downloadFile(data, name, mime) {469var a = document.createElement("a");470a.download = name;471a.href = URL.createObjectURL(new Blob([data], {type: mime || "application/octet-stream"}));472a.click();473window.setTimeout(function() {474URL.revokeObjectURL(a.href);475}, 2000);476}477478function uploadFile(accept, callback) {479var input = document.createElement("input");480input.type = "file";481input.accept = accept;482input.onchange = async function() {483var file = this.files[0];484var data = await readFile(file);485callback({name: file.name, data: data});486}487input.click();488}489490function uploadFileMulti(accept, callback) {491let directoryUpload = confirm("Upload a directory?");492493var input = document.createElement("input");494input.type = "file";495if (directoryUpload) {496input.setAttribute("directory", "");497input.setAttribute("webkitDirectory", "");498} else {499input.setAttribute("multiple", "");500input.accept = accept;501}502input.onchange = async function() {503let datas = [];504for (var i = 0; i < this.files.length; i++) {505var name = directoryUpload ? (this.files[i].relativePath || this.files[i].webkitRelativePath || "").split("/").slice(1).join("/") : this.files[i].name;506var data = await readFile(this.files[i]);507datas.push({path: name, data: data});508if (i == this.files.length - 1 && callback) callback(datas);509}510}511input.click();512}513514// scripts515function getScript(url, callback, err) {516var script = document.createElement("script");517script.type = "text/javascript";518script.src = url;519script.onload = function() {520if (callback) callback();521}522script.onerror = function(e) {523document.body.removeChild(script);524if (err) err(e);525}526document.body.appendChild(script);527}528529function getCore(name, callback, err) {530getScript(relativeBase + coreDir + name + "_libretro.js", callback, err);531}532533// check for updates534function checkForUpdates() {535grab(infoJsonUrl, "text", function(text) {536try {537var updateObj = JSON.parse(text);538if (updateObj.webretro) {539latestVersion = updateObj.webretro;540if (updateObj.versions[webretroVersion.toString()]) versionIndicator.title = "New features in this version:\n\n- " + updateObj.versions[webretroVersion.toString()].changeList.join("\n- ");541if (latestVersion > webretroVersion && updateObj.versions[latestVersion.toString()]) {542updateNotice.textContent = "New webretro version available: v" + latestVersion.toString() + ". Features:\n\n- " + updateObj.versions[latestVersion.toString()].changeList.join("\n- ") + "\n\nThe site owner(s) can apply the update.";543updateNotice.style.display = "initial";544}545}546} catch (e) {547console.warn(e);548}549});550}551552// download standalone webretro553function downloadStandaloneWebretro() {554grab(standaloneDownloadUrl, "text", function(data) {555alert("Downloading standalone webretro. Internet is required for operation.");556downloadFile(data, standaloneDownloadUrl.split("/").slice(-1)[0], "text/html");557}, function() {558alert("Failed to fetch file.");559});560}561562downloadStandaloneButton.onclick = downloadStandaloneWebretro;563564function getFileExtsForCore(core) {565return [fileExts[systems[core]], (cdromCores.includes(core) ? cdromExts : ""), (playlistCores.includes(core) ? playlistExts : "")].filter(i => i).join(", ");566}567568// unzip file569function unzipFile(data, exts, callback, empty, notfound) {570new zip.ZipReader(new zip.Uint8ArrayReader(new Uint8Array(data))).getEntries().then(async function(entries) {571if (entries.length) {572for (var i = 0; i < entries.length; i++) {573if (!entries[i].directory && exts.split(", ").includes("." + u8atoutf8(entries[i].rawFilename).split(".").slice(-1)[0].toLowerCase())) {574var name = u8atoutf8(entries[i].rawFilename);575var uzd = await entries[i].getData(new zip.Uint8ArrayWriter());576callback(name, uzd.buffer);577break;578}579if (i == entries.length - 1 && notfound) notfound();580}581} else if (empty) empty();582});583}584585// unzip all files586function unzipFileMulti(data, callback, empty) {587new zip.ZipReader(new zip.Uint8ArrayReader(new Uint8Array(data))).getEntries().then(async function(entries) {588if (entries.length) {589let datas = [];590for (var i = 0; i < entries.length; i++) {591if (!entries[i].directory) {592var name = u8atoutf8(entries[i].rawFilename);593var uzd = await entries[i].getData(new zip.Uint8ArrayWriter());594datas.push({path: name, data: uzd.buffer});595if (i == entries.length - 1 && callback) callback(datas);596}597}598} else if (empty) empty();599});600}601602// zip files603async function zipFiles(files, callback) {604var u8aWriter = new zip.Uint8ArrayWriter("application/zip");605var writer = new zip.ZipWriter(u8aWriter);606for (var i = 0; i < files.length; i++) {607await writer.add(files[i].path, new zip.Uint8ArrayReader(new Uint8Array(files[i].data)));608}609await writer.close();610var zipped = await u8aWriter.getData();611callback(zipped.buffer);612}613614// file renames615function replaceInFiles(files, find, replace) {616return files.map(i => ({path: i.path.replace(find, replace), data: i.data}));617}618619// uauth uploads620function handleWebFile(data) {621if (data.message == "success") {622ffd.style.display = "none";623romUploadCallback([{path: data.name, data: data.data}]);624} else if (data.message == "error") {625alert("There was an error with the file picker. This may mean that you have to allow popup windows.");626}627}628629function uploadWebFile(type, exts) {630uauth.open(type, exts.split(", "), handleWebFile);631}632633// file tree to list, etc (for drag-and-drop files)634function readFileEntry(fileEntry) {635return new Promise(function(resolve) {636fileEntry.file(function(file) {637resolve(file);638});639});640}641642function readDirectoryEntry(directoryEntry) {643return new Promise(function(resolve) {644directoryEntry.createReader().readEntries(function(entries) {645resolve(entries);646});647});648}649650async function fileTreeToList(items) {651let newItems = [];652for (var i = 0; i < items.length; i++) {653if (items[i].isFile) {654newItems.push(items[i]);655} else if (items[i].isDirectory) {656var entries = await readDirectoryEntry(items[i]);657var contents = await fileTreeToList(entries);658newItems = newItems.concat(contents);659}660}661return newItems;662}663664// rom upload665function readyRomUploads(exts) {666romUploadsReady = true;667668// when a rom file is chosen669upload.onclick = function() {670if (multiFileCores.includes(core)) {671uploadFileMulti(exts, function(files) {672ffd.style.display = "none";673log("Succesfully read ROM files...");674romUploadCallback(files);675});676} else {677uploadFile(exts, function(file) {678ffd.style.display = "none";679log('Succesfully read ROM file "' + file.name + '"');680romUploadCallback([{path: file.name, data: file.data}]);681});682}683}684685// web uploads686googleDriveUpload.onclick = function() {687uploadWebFile("drive", exts);688}689dropboxUpload.onclick = function() {690uploadWebFile("dropbox", exts);691}692oneDriveUpload.onclick = function() {693uploadWebFile("onedrive", exts);694}695696// file drop (we need these to be global so they can be removed later)697window.fileDragEnter = function(e) {698if (e.dataTransfer.types.includes("Files")) ffd.classList.add("filehover");699}700window.fileDragOver = function(e) {701e.preventDefault();702}703window.fileDropped = async function(e) {704if (e.dataTransfer.types.includes("Files")) {705e.preventDefault();706ffd.style.display = "none";707708let fileTree = Array.from(e.dataTransfer.items).map(i => i.webkitGetAsEntry());709let files = await fileTreeToList(fileTree);710let datas = [];711712for (var i = 0; i < files.length; i++) {713var file = await readFileEntry(files[i]);714var name = files[i].fullPath.slice(1);715var data = await readFile(file);716datas.push({path: name, data: data});717}718719// extract inside if only 1 directory is dropped720if (fileTree.length == 1 && fileTree[0].isDirectory) {721for (var i = 0; i < datas.length; i++) {722datas[i].path = datas[i].path.split("/").slice(1).join("/");723}724}725726log("Succesfully read ROM file(s)...");727romUploadCallback(datas);728}729}730document.addEventListener("dragenter", fileDragEnter, false);731document.addEventListener("dragover", fileDragOver, false);732document.addEventListener("drop", fileDropped, false);733}734735// chrome 102 launch queue https://developer.chrome.com/blog/new-in-chrome-102/#file-handlers736function readyLaunchQueue() {737if ("launchQueue" in window && LaunchParams && "files" in LaunchParams.prototype) {738launchQueue.setConsumer(async function(params) {739log("Launching with ROM file(s)...");740ffd.style.display = "none";741742let datas = [];743744for (var i = 0; i < params.files.length; i++) {745var file = await params.files[i].getFile();746var data = await readFile(file);747datas.push({path: params.files[i].name, data: data});748}749750log("Succesfully read ROM file(s)...");751romUploadCallback(datas);752});753}754}755756// rom fetch757function readyRomFetch() {758var romloc = (/^(https?:)?\/\//i).test(queries.rom) ? queries.rom : relativeBase + "roms/" + queries.rom;759var romFilename = queries.rom.split("/").slice(-1)[0];760grab(romloc, "arraybuffer", function(data) {761log("Succesfully fetched ROM from " + romloc);762romMode = "querystring";763romUploadCallback([{path: romFilename, data: data}]);764}, function(error) {765alert("Could not get ROM at " + romloc + " (Error " + error + ")");766romMode = "upload";767ffd.style.display = "block";768});769}770771// safe writeFile772function safeWriteFile(path, data) {773FS.createPath("/", path.split("/").slice(1, -1).join("/"), true, true);774return FS.writeFile(path, data);775}776777function uploadNCreate() {778uploadFile("", function(file) {779FS.writeFile("/" + file.name, new Uint8Array(file.data));780});781}782783// console window784var conw = new jswindow({title: "Console", icon: "assets/terminal.svg"});785786var wconsole = document.createElement("textarea");787wconsole.classList.add("console");788wconsole.setAttribute("spellcheck", "false");789wconsole.setAttribute("readonly", "");790791wconsole.wconsolemarker = document.createElement("span");792wconsole.wconsolemarker.classList.add("consolemarker");793794wconsole.wconsoleinput = document.createElement("input");795wconsole.wconsoleinput.type = "text";796wconsole.wconsoleinput.classList.add("consoleinput");797wconsole.wconsoleinput.title = "You can type things here as though you were using the browser console.";798wconsole.wconsoleinput.setAttribute("spellcheck", "false");799wconsole.wconsolemarker.onclick = function() { wconsole.wconsoleinput.focus(); }800wconsole.wconsoleinput.onkeydown = async function(e) {801e.stopPropagation();802if (e.isTrusted && e.code == "Enter") {803log(await eval("(async function() { return " + this.value + " })()"), this.value);804this.value = "";805}806}807808conw.innerWindow.appendChild(wconsole);809conw.innerWindow.appendChild(wconsole.wconsolemarker);810conw.innerWindow.appendChild(wconsole.wconsoleinput);811812consoleButton.onclick = function() {813conw.open({width: 450, height: 250, left: 100, top: 50});814wconsole.wconsoleinput.focus();815wconsole.scrollTo({top: wconsole.scrollHeight});816}817818if (queries.hasOwnProperty("console")) conw.open({width: 450, height: 250, left: 100, top: 50});819820// fullscreen button821fullscreenButton.onclick = function() {822if (document.fullscreenElement) {823document.exitFullscreen();824} else {825document.body.requestFullscreen();826}827}828829// theme selector830function setTheme(theme) {831if (themes.hasOwnProperty(theme)) {832document.body.dataset.theme = themes[theme].id;833menuHeight = themes[theme].menuHeight;834adjustMenuHeight();835}836}837838currentTheme = localStorage.getItem("webretro_settings_theme") || defaultTheme;839setTheme(currentTheme);840try {841themeSelector.querySelector("[value=" + currentTheme + "]").checked = true;842} catch (e) {843console.warn(e);844}845846themeSelector.onchange = function(e) {847currentTheme = e.target.value;848setTheme(currentTheme);849localStorage.setItem("webretro_settings_theme", currentTheme);850}851852// modal windows (managers)853function openManager(type) {854if (managers[type]) {855if (managerClosed[currentManager]) managerClosed[currentManager]();856currentManager = type;857if (managerOpened[type]) managerOpened[type]();858managerTitle.textContent = capitalize(managerNames[type] || type + "s");859clearManagers();860managers[type].style.display = "block";861modals.style.display = "block";862}863}864865function clearManagers() {866Object.values(managers).forEach(function(e) {867e.style.display = "none";868});869}870871function closeManagers() {872modals.style.display = "none";873clearManagers();874managerTitle.textContent = "";875if (managerClosed[currentManager]) managerClosed[currentManager]();876currentManager = undefined;877}878879managerClose.onclick = closeManagers;880881// --- code for the keybind manager ---882883// convert between config strings and objects884function configStrToObj(str) {885var convert1 = str.slice(0, -1).split("\n");886var convert2 = {};887for (var i = 0; i < convert1.length; i++) {888var convert3 = convert1[i].split(" = ");889convert2[convert3[0]] = convert3[1].slice(1, -1);890}891return convert2;892}893894function configObjToStr(obj) {895var convert1 = Object.keys(obj);896var convert2 = "";897for (var i = 0; i < convert1.length; i++) {898convert2 += convert1[i] + ' = "' + obj[convert1[i]] + '"\n';899}900return convert2;901}902903// load config saved in localStorage904var defaultKeybindsObj = configStrToObj(defaultKeybinds);905var savedKeybindsObj = localStorage.getItem("RetroArch_settings_keybinds") ? Object.assign(Object.assign({}, defaultKeybindsObj), configStrToObj(localStorage.getItem("RetroArch_settings_keybinds"))) : Object.assign({}, defaultKeybindsObj);906var keybindsObj = Object.assign({}, savedKeybindsObj);907908var validKeybinds = Object.keys(defaultKeybindsObj);909910// update the config list911function createConfigList() {912keybindTable.innerHTML = "";913// make the list914for (var i = 0; i < validKeybinds.length; i++) {915keybindTable.innerHTML += "<tr><td>" + validKeybinds[i].replace(/^input_/, "") + "</td><td>" + keybindsObj[validKeybinds[i]] + "</td></tr>";916}917// highlight conflicting keys918var keysList = Object.values(keybindsObj);919for (var i = 0; i < validKeybinds.length; i++) {920var matches = keysList.filter(v => v == keybindsObj[validKeybinds[i]]);921if (matches.length > 1 && !(matches[0] == "nul")) keybindTable.children[i].lastElementChild.classList.add("conflict");922}923}924925// rebinding a key926keybindTable.onclick = function(e) {927if (e.target.tagName == "TD" && !e.target.nextElementSibling) {928let valueElement = e.target;929let keyNo = Array.from(keybindTable.children).indexOf(e.target.parentElement);930valueElement.classList.remove("conflict");931valueElement.textContent = "press a key (escape to unbind)";932933function newKeyHandler(e) {934if (e.code == "Escape") {935keybindsObj[validKeybinds[keyNo]] = "nul";936createConfigList();937} else {938keybindsObj[validKeybinds[keyNo]] = codeToConfigIDMap[e.code] || "nul";939createConfigList();940}941finishKeybindInput();942}943function cancelKeybindInput() {944finishKeybindInput();945createConfigList();946}947function finishKeybindInput() {948document.removeEventListener("keydown", newKeyHandler);949document.removeEventListener("mousedown", cancelKeybindInput);950}951document.addEventListener("keydown", newKeyHandler, false);952document.addEventListener("mousedown", cancelKeybindInput, false);953}954}955956function tryApplyConfig() {957if (mainCompleted) {958FS.writeFile("/home/web_user/retroarch/userdata/retroarch.cfg", nulKeys + configObjToStr(savedKeybindsObj) + extraConfig);959Module._cmd_reload_config();960}961}962963// save the keybinds to localStorage, and apply them964saveKeybinds.onclick = function() {965savedKeybindsObj = Object.assign({}, keybindsObj);966localStorage.setItem("RetroArch_settings_keybinds", configObjToStr(savedKeybindsObj));967tryApplyConfig();968alert("Saved!");969}970971resetKeybinds.onclick = function() {972if (confirm("Are you sure you want to reset all of the keybinds to their default values?")) {973savedKeybindsObj = Object.assign({}, defaultKeybindsObj);974keybindsObj = Object.assign({}, savedKeybindsObj);975localStorage.removeItem("RetroArch_settings_keybinds");976createConfigList();977tryApplyConfig();978}979}980981// --- code for the screenshot manager ---982983// zip and download all of the screenshots in the list984downloadAllScreenshots.onclick = function() {985if (screenshotDatas.length) {986zipFiles(replaceInFiles(screenshotDatas, "rom", romName), function(zd) {987downloadFile(zd, "screenshots-" + getTime() + ".zip", "application/zip");988});989} else {990alert("There are no screenshots to download!");991}992}993994// update the screenshot list995function createScreenshotList() {996var screenshots = FS.analyzePath("/home/web_user/retroarch/userdata/screenshots/").exists ? FS.readdir("/home/web_user/retroarch/userdata/screenshots/").filter(k => ![".", ".."].includes(k)) : [];997screenshotsDiv.innerHTML = "";998999for (var i = 0; i < screenshots.length; i++) {1000var screenshotData = FS.readFile("/home/web_user/retroarch/userdata/screenshots/" + screenshots[i]);1001var blobUrl = window.URL.createObjectURL(new Blob([screenshotData], {type: "image/png"}));1002screenshotDatas[i] = {path: screenshots[i], data: screenshotData.buffer};1003screenshotObjUrls[i] = blobUrl;1004screenshotsDiv.innerHTML += '<div class="screenshot"><img src="' + blobUrl + '"><input type="button" data-action="download" value="Download"><input type="button" data-action="delete" value="Delete">' + "</div>";1005}1006}10071008// why I didn't just use the DOM? I don't know1009screenshotsDiv.onclick = function(e) {1010if (e.target.tagName == "INPUT") {1011var screenshotNo = Array.from(screenshotsDiv.children).indexOf(e.target.parentElement);1012switch (e.target.dataset.action) {1013case "download":1014downloadFile(screenshotDatas[screenshotNo].data, screenshotDatas[screenshotNo].path.replace("rom", romName), "image/png");1015break;1016case "delete":1017if (confirm("Are you sure you want to delete this screenshot?")) {1018// doing all this is probably more efficient then reloading all of the screenshots1019window.URL.revokeObjectURL(screenshotObjUrls[screenshotNo]);1020FS.unlink("/home/web_user/retroarch/userdata/screenshots/" + screenshotDatas[screenshotNo].path);1021screenshotObjUrls.splice(screenshotNo, 1);1022screenshotDatas.splice(screenshotNo, 1);1023e.target.parentElement.remove();1024}1025break;1026}1027}1028}10291030// --- code for the save/state manager ---10311032function updateQuotaDisplay() {1033navigator.storage.estimate().then(function(info) {1034quotaText.textContent = "Storage used (estimate): " + bytesToHumanReadable(info.usage) + " / " + bytesToHumanReadable(info.quota) + " (" + (info.usage / info.quota).toFixed(2) + "%)";1035});1036}10371038// update the save list1039function createSaveList() {1040updateQuotaDisplay();1041getAllIdbItems().then(function(items) {1042saveTable.innerHTML = "";1043// make the list1044for (var i = 0; i < items.length; i++) {1045if ((/^RetroArch_(saves|states)_/).test(items[i].key)) {1046var sName = items[i].key.replace(/^RetroArch_(saves|states)_/, "");1047var sType = (/^RetroArch_saves_/).test(items[i].key) ? "save" : "state";1048saveIDs.push({id: items[i].key, name: sName, type: sType});1049saveTable.innerHTML += "<tr><td>" + capitalize(sType) + ": " + sName + '</td><td><span data-action="download">Download</span><span data-action="delete">Delete</span></td></tr>';1050}1051}1052});1053}10541055saveTable.onclick = function(e) {1056if (e.target.tagName == "SPAN") {1057let saveNo = Array.from(saveTable.children).indexOf(e.target.parentElement.parentElement);1058switch (e.target.dataset.action) {1059case "download":1060getIdbItem(saveIDs[saveNo].id).then(function(data) {1061if (saveIDs[saveNo].type == "save") {1062var files = replaceInFiles(saveArrToFiles(data), "ROMNAME", saveIDs[saveNo].name);1063if (files.length == 1) {1064downloadFile(files[0].data, "game-sram-" + saveIDs[saveNo].name + "-" + getTime() + "." + files[0].path.split(".").slice(1).join("."));1065} else {1066zipFiles(files, function(zd) {1067downloadFile(zd, "game-sram-" + saveIDs[saveNo].name + "-" + getTime() + ".zip", "application/zip");1068});1069}1070} else {1071downloadFile(data, "game-state-" + saveIDs[saveNo].name + "-" + getTime() + ".state");1072}1073});1074break;1075case "delete":1076if (confirm("Are you sure you want to delete this " + saveIDs[saveNo].type + ' for "' + saveIDs[saveNo].name + '"?') && confirm("Really really sure?")) {1077removeIdbItem(saveIDs[saveNo].id);1078saveIDs.splice(saveNo, 1);1079e.target.parentElement.parentElement.remove();1080updateQuotaDisplay();1081}1082break;1083}1084}1085}10861087// --- master rom selector ---10881089function getMasterRom(files) {1090return new Promise(function(resolve) {1091// some auto detecting1092var recommendedExts = "";1093if (playlistCores.includes(core)) {1094recommendedExts = playlistExts;1095} else if (["dosbox", "dosbox_pure", "scummvm"].includes(core)) {1096recommendedExts = ".exe, .bat, .com";1097}1098if (recommendedExts) {1099var recommendedExtsArray = recommendedExts.split(", ");1100var detectedFiles = files.filter(i => recommendedExtsArray.includes("." + i.path.toLowerCase().split(".").slice(-1)[0]));1101// if ONLY one match is found, use it1102if (detectedFiles.length == 1) {1103resolve(files.indexOf(detectedFiles[0]));1104return;1105}1106}11071108openManager("romSelector");1109if (recommendedExts) {1110recommendedExtensions.textContent = "Recommended file extensions: " + recommendedExts;1111} else {1112romSelectorTable.parentElement.classList.add("fulltableparent");1113}1114romSelectorTable.innerHTML = "";1115// make the list1116for (var i = 0; i < files.length; i++) {1117romSelectorTable.innerHTML += "<tr><td>" + files[i].path + "</td></tr>";1118}1119romSelectorTable.onclick = function(e) {1120closeManagers();1121resolve(Array.from(romSelectorTable.children).indexOf(e.target.parentElement));1122return;1123}1124});1125}11261127// --- end manager-specific code ---11281129var managerOpened = {1130"keybind": function() {1131createConfigList();1132},1133"screenshot": function() {1134createScreenshotList();1135},1136"save": function() {1137createSaveList();1138},1139"romSelector": function() {1140managerClose.style.display = "none";1141}1142};11431144var managerClosed = {1145"keybind": function() {1146keybindsObj = Object.assign({}, savedKeybindsObj);1147},1148"screenshot": function() {1149// clear the blob: urls used for the screenshots1150for (var i = 0; i < screenshotObjUrls.length; i++) {1151window.URL.revokeObjectURL(screenshotObjUrls[i]);1152}1153screenshotObjUrls = [];1154screenshotDatas = [];1155},1156"save": function() {1157saveIDs = [];1158},1159"romSelector": function() {1160managerClose.style.display = "initial";1161}1162};11631164// opening the managers11651166keybindsButton.onclick = function(e) {1167e.preventDefault();1168openManager("keybind");1169}11701171screenshotsButton.onclick = function(e) {1172e.preventDefault();1173openManager("screenshot");1174}11751176savesButton.onclick = function(e) {1177e.preventDefault();1178openManager("save");1179}11801181statesButton.onclick = function(e) {1182e.preventDefault();1183openManager("save");1184};11851186// ---------- START LOAD ----------1187(function() {1188versionIndicator.textContent = "v" + webretroVersion.toString();1189checkForUpdates();11901191// ?system query1192if (!queries.core && queries.system) {1193var detectedCores = allCores.filter(k => systems[k].toLowerCase() == queries.system.toLowerCase());1194var usableCores = installedCores.filter(k => systems[k].toLowerCase() == queries.system.toLowerCase());1195var usingCore = usableCores.find(k => preferredCores.includes(k)) || usableCores[0];1196if (usingCore) {1197queries.core = usingCore;1198} else if (queries.system.toLowerCase() == "autodetect") {1199queries.core = "autodetect";1200} else if (!detectedCores.length) {1201alert('Could not find any cores matching the system "' + queries.system + '".');1202} else {1203alert("Found the core(s) " + detectedCores.join(", ") + ", but none were marked as installed.");1204}1205}12061207// ?core query1208if (queries.core) {1209try {1210if (!window.chrome) alert("Best performance on Chrome!");1211} catch (e) {1212console.warn(e);1213}12141215// show menu bar1216menuBar.style.display = "block";12171218if (queries.core.toLowerCase() == "autodetect") {1219romUploadCallback = autodetectCoreHandler;1220systemName.textContent = "";1221readyRomUploads(".zip, " + allFileExts);12221223document.addEventListener("DOMContentLoaded", readyLaunchQueue, false);1224} else {1225romUploadCallback = initFromFile;1226core = queries.core;12271228setStatus("Getting core");1229// detect system for ROM upload1230systemName.textContent = systems[core] || "";12311232// add an s to the upload button if using a multifile core1233if (multiFileCores.includes(core)) upload.value += "s";12341235// core github link1236if (coreGithubLinks[core]) {1237coreGithub.style.setProperty("display", "inline-block", "important");1238coreGithub.href = "https://github.com/" + coreGithubLinks[core];1239}12401241// show the pre-start options1242if (coreOptions[core]) {1243pso.style.display = "block";1244try {1245pso.querySelector("[data-core=" + core + "]").style.display = "block";1246} catch (e) {1247console.warn(e);1248}1249}12501251getCore(core, function() {1252removeStatus("Getting core");1253log("Got core: " + core);1254if (romMode != "querystring") document.title = (coreNames[core] || core) + (appIsPwa ? "" : " | webretro");12551256readyRomUploads([".zip" + (exclusiveMultiFileCores.includes(core) ? "" : ", .bin"), (allCores.includes(core) ? getFileExtsForCore(core) : allFileExts)].filter(i => i).join(", "));1257}, function() {1258// core loading error1259alert('Could not load specified core "' + core + '". Here is a list of available cores.');1260showCoreList();1261});1262}12631264// ?rom query1265if (queries.rom) {1266readyRomFetch();1267} else {1268// prompt user to upload ROM file1269romMode = "upload";1270ffd.style.display = "block";1271}1272} else {1273// no core specified1274showCoreList();1275}1276})();1277// ----------- END LOAD -----------12781279// start emulator from file(s)1280function initFromFile(files) {1281if (files.length == 1 && files[0].path.split(".").slice(-1)[0].toLowerCase() == "zip") {1282if (multiFileCores.includes(core)) {1283log("Zip file detected, unzipping... (multi-file ROM detected... probably)");12841285unzipFileMulti(files[0].data, function(dataArr) {1286readyForInit(dataArr);1287}, function() {1288alert("That zip file appears to be empty!");1289});1290} else {1291log("Zip file detected, unzipping... (single-file ROM detected)");12921293unzipFile(files[0].data, [".bin", getFileExtsForCore(core)].filter(i => i).join(", "), function(name, contents) {1294readyForInit([{path: name, data: contents}]);1295}, function() {1296alert("That zip file appears to be empty!");1297}, function() {1298alert("Couldn't find a valid ROM file in that zip file. Are you using the right core? This is " + systems[core] + ". (The ROM has to be at the base directory of the zip file)");1299});1300}1301} else {1302readyForInit(files);1303}1304}13051306// autodetect core mode1307function autodetectCoreHandler(files) {1308if (files.length == 1) {1309if (files[0].path.split(".").slice(-1)[0].toLowerCase() == "zip") {1310log("Zip file detected, unzipping...");13111312unzipFile(files[0].data, allFileExts, function(name, contents) {1313autodetectCore(name, contents);1314}, function() {1315alert("That zip file appears to be empty!");1316}, function() {1317alert("Couldn't find a valid ROM file in that zip file. (The ROM has to be at the base directory of the zip file)");1318});1319} else {1320autodetectCore(files[0].path, files[0].data);1321}1322} else {1323alert("Unable to autodetect when multiple files are chosen");1324}1325}13261327function autodetectCore(name, data) {1328var nameExt = "." + name.split(".").slice(-1)[0].toLowerCase();13291330var detectedSystem = allSystems.find(k => fileExts[k].split(", ").includes(nameExt));13311332var detectedCores = allCores.filter(k => systems[k] == detectedSystem);1333var usableCores = installedCores.filter(k => systems[k] == detectedSystem);1334var usingCore = usableCores.find(k => preferredCores.includes(k)) || usableCores[0];13351336if (usingCore) {1337core = usingCore;13381339setStatus("Getting core");13401341// show the pre-start options1342if (coreOptions[core]) {1343pso.style.display = "block";1344try {1345pso.querySelector("[data-core=" + core + "]").style.display = "block";1346} catch (e) {1347console.warn(e);1348}1349}13501351getCore(core, function() {1352removeStatus("Getting core");1353log("Got core: " + core);1354readyForInit([{path: name, data: data}]);1355});1356} else if (!detectedCores.length) {1357alert('Unrecognized file extension "' + nameExt + '". This does not mean that it is unsupported, it may just mean that it is not auto-detectable.');1358} else {1359alert("Found the core(s) " + detectedCores.join(", ") + " for system " + detectedSystem + ", but none were marked as installed.");1360}1361}13621363// if the ROM is specified in the querystring, we will need to wait until the user has clicked to start the emulator https://goo.gl/7K7WLu1364function readyForInit(files) {1365// undefine romUploadCallback to make sure initialization only happens once (it shouldn't anyway)1366romUploadCallback = function() {};13671368// set the romName now if using single-file rom1369if (files.length == 1) {1370romName = files[0].path.split("/").slice(-1)[0].split(".")[0];1371document.title = romName + (appIsPwa ? "" : " | webretro");1372}13731374if (queries.romshift) {1375let shift = parseInt(queries.romshift);1376for (var i = 0; i < files.length; i++) {1377files[i].data = avShift(new Uint8Array(files[i].data), shift).buffer;1378}1379}13801381// remove the file drop listeners1382if (romUploadsReady) {1383document.removeEventListener("dragenter", fileDragEnter);1384document.removeEventListener("dragover", fileDragOver);1385document.removeEventListener("drop", fileDropped);1386}13871388if (romMode == "querystring" && (queries.hasOwnProperty("forcestartbutton") || !isAudioAllowed())) {1389// start button (don't delete this section, audio contexts are not allowed to start until a user gesture on the page, in this case, clicking the start button) https://goo.gl/7K7WLu1390startButton.style.display = "initial";1391startButton.onclick = function() {1392startButton.style.display = "none";1393initFromData(files);1394}1395} else {1396initFromData(files);1397}1398}13991400// prepare FS with bundle1401function prepareBundle() {1402setStatus("Getting assets");1403log("Starting bundle fetch");1404let bundleSTime = performance.now();14051406grab(bundleCdnLatest + "bundle/indexedfiles-v2.txt", "text", function(data) {1407try {1408var splitData = data.split(",,,\n");1409fsBundleDirs = JSON.parse(splitData[0]);1410fsBundleFiles = splitData[1].split("\n");14111412// make the paths1413FS.createPath("/", baseFsBundleDir.substring(1), true, true);1414for (var i = 0; i < fsBundleDirs.length; i++) {1415FS.createPath(baseFsBundleDir + fsBundleDirs[i][0], fsBundleDirs[i][1], true, true);1416}14171418loadingBar.style.display = "initial";1419loadingBar.value = 0;1420let step = 1 / fsBundleFiles.length;1421let num = 0;14221423// make the files1424for (let i = 0; i < fsBundleFiles.length; i++) {1425grab(bundleCdn + "bundle" + fsBundleFiles[i], "arraybuffer", function(data) {1426FS.writeFile(baseFsBundleDir + fsBundleFiles[i], new Uint8Array(data));1427loadingBar.value += step;1428if (++num == fsBundleFiles.length) donePreparingBundle(performance.now() - bundleSTime);1429}, function() {1430bundleErrors++;1431loadingBar.value += step;1432if (++num == fsBundleFiles.length) donePreparingBundle(performance.now() - bundleSTime);1433});1434}1435} catch (e) {1436console.warn(e);1437log("Failed to get asset bundle, skipping");1438bundleReady = true;1439removeStatus("Getting assets");1440}1441}, function() {1442log("Failed to get asset bundle, skipping");1443bundleReady = true;1444removeStatus("Getting assets");1445});1446}14471448function donePreparingBundle(tooktime) {1449loadingBar.style.display = "none";1450extraConfig += 'menu_minimal_assets = "true"\n';1451bundleReady = true;1452removeStatus("Getting assets");1453log("Finished bundle fetch in " + (tooktime / 1000).toFixed(1) + " seconds, " + bundleErrors + " errors");1454}14551456// prepare FS with BIOSes1457function prepareBios() {1458if (bioses[core]) {1459let bios = bioses[core];1460let num = 0;14611462FS.createPath("/", baseFsSystemDir.substring(1) + bios.path, true, true);1463for (let i = 0; i < bios.files.length; i++) {1464grab(biosCdn + bios.files[i], "arraybuffer", function(data) {1465FS.writeFile(baseFsSystemDir + bios.path + bios.files[i], new Uint8Array(data));1466log("BIOS fetch: Success " + bios.files[i]);1467if (++num == bios.files.length) biosReady = true;1468}, function() {1469log("BIOS fetch: Failed " + bios.files[i]);1470if (++num == bios.files.length) biosReady = true;1471});1472}1473} else {1474biosReady = true;1475}1476}14771478// tell the user to not rename the rom1479function doNotRename() {1480if (romMode == "upload" && !localStorage.getItem("webretro_settings_pastFirstSave")) {1481alert("WARNING: Do not rename your ROM file after this! The save data is specific to the ROM file name!");1482localStorage.setItem("webretro_settings_pastFirstSave", "true");1483}1484}14851486// converting save lists1487function saveArrToObj(arr) {1488let obj = {};1489for (var i = 0; i < arr.length; i++) {1490obj[arr[i].dir + "ROMNAME" + arr[i].ext] = arr[i].data;1491}1492return obj;1493}14941495function saveObjToArr(obj) {1496return Object.entries(obj).map(i => ({ext: i[0].split("ROMNAME")[1], dir: i[0].split("ROMNAME")[0], data: i[1]}));1497}14981499function saveArrToFiles(arr) {1500let files = [];1501for (var i = 0; i < arr.length; i++) {1502files.push({path: arr[i].dir + "ROMNAME" + arr[i].ext, data: arr[i].data.buffer});1503}1504return files;1505}15061507function saveFilesToArr(files) {1508let arr = [];1509for (var i = 0; i < files.length; i++) {1510arr.push({ext: files[i].path.split("ROMNAME")[1], dir: files[i].path.split("ROMNAME")[0], data: new Uint8Array(files[i].data)});1511}1512return arr;1513}15141515// save game1516function saveSRAMHandler(path) {1517saveObj[path.replace(baseFsSaveDir, "").replace("rom", "ROMNAME")] = FS.readFile(path);1518setIdbItem("RetroArch_saves_" + romName, saveObjToArr(saveObj));15191520new sideAlert("Saved", 3000);15211522doNotRename();1523}15241525// save state1526function saveStateHandler() {1527if (FS.analyzePath("/home/web_user/retroarch/userdata/states/rom.state").exists) {1528setIdbItem("RetroArch_states_" + romName, FS.readFile("/home/web_user/retroarch/userdata/states/rom.state"));15291530doNotRename();1531} else {1532new sideAlert("There was an error saving state. Please try again.", 5000);1533}1534}15351536// autosaving1537function autosaveSRAM() {1538if (autosave.checked && !document.hidden && !isPaused) {1539new sideAlert("Autosaving...", 3000);1540Module._cmd_savefiles();1541}1542window.setTimeout(function() {1543autosaveSRAM();1544}, 300000);1545}15461547// writeToFile router1548function writeToFileHandler(path) {1549// console.log("%c" + path, "color: #8888ff");15501551if (path.startsWith(baseFsSaveDir)) {1552saveSRAMHandler(path);1553} else if (path.startsWith("/home/web_user/retroarch/userdata/states/")) {1554if (path == "/home/web_user/retroarch/userdata/states/rom.state") saveStateHandler();1555}1556}15571558// runs after emulator starts1559function afterStart() {1560mainCompleted = true;15611562adjustCanvasSize();1563menuBar.classList.add("show");15641565// functions for save and state buttons15661567// states15681569saveState.classList.remove("disabled");1570saveState.onclick = function() {1571Module._cmd_save_state();1572}15731574importState.classList.remove("disabled");1575importState.onclick = function() {1576if (noStateCores.includes(core)) {1577alert("Core does not support save states.");1578} else {1579uploadFile(".bin, .state", function(file) {1580setIdbItem("RetroArch_states_" + romName, new Uint8Array(file.data));1581FS.writeFile("/home/web_user/retroarch/userdata/states/rom.state", new Uint8Array(file.data));1582new sideAlert("Imported state (press load state)", 3000);1583});1584}1585}15861587loadState.classList.remove("disabled");1588loadState.onclick = function() {1589Module._cmd_load_state();1590}15911592exportState.classList.remove("disabled");1593exportState.onclick = function() {1594if (FS.analyzePath("/home/web_user/retroarch/userdata/states/rom.state").exists) {1595downloadFile(FS.readFile("/home/web_user/retroarch/userdata/states/rom.state"), "game-state-" + romName + "-" + getTime() + ".state");1596} else {1597alert("No state to export.");1598}1599}16001601undoSaveState.classList.remove("disabled");1602undoSaveState.onclick = function() {1603Module._cmd_undo_save_state();1604}16051606undoLoadState.classList.remove("disabled");1607undoLoadState.onclick = function() {1608Module._cmd_undo_load_state();1609}16101611// saves16121613saveGame.classList.remove("disabled");1614saveGame.onclick = function() {1615new sideAlert("Saving...", 3000);1616Module._cmd_savefiles();1617}16181619importSave.classList.remove("disabled");1620importSave.onclick = function() {1621if (noSaveCores.includes(core)) {1622alert("Core does not support SRAM.");1623} else {1624function done() {1625if (confirm("Save imported. Reloading now for changes to take effect.")) {1626autosave.checked = false;1627window.onbeforeunload = function() {}1628window.location.reload();1629}1630}1631if (multiSaveCores.includes(core)) {1632uploadFileMulti(".zip, .bin, " + sramExts, function(files) {1633if (files.length == 1) {1634if (files[0].path.split(".").slice(-1)[0].toLowerCase() == "zip") {1635unzipFileMulti(files[0].data, function(uzfiles) {1636setIdbItem("RetroArch_saves_" + romName, saveFilesToArr(replaceInFiles(uzfiles, romName, "ROMNAME")));1637done();1638}, function() {1639alert("Zip File is empty");1640});1641} else {1642setIdbItem("RetroArch_saves_" + romName, [{ext: "." + files[0].path.split(".").slice(-1)[0], dir: "", data: new Uint8Array(file.data)}]);1643done();1644}1645} else {1646setIdbItem("RetroArch_saves_" + romName, saveFilesToArr(replaceInFiles(files, romName, "ROMNAME")));1647done();1648}1649});1650} else {1651uploadFile(".bin, " + sramExts, function(file) {1652setIdbItem("RetroArch_saves_" + romName, [{ext: "." + file.name.split(".").slice(-1)[0], dir: "", data: new Uint8Array(file.data)}]);1653done();1654});1655}1656}1657}16581659exportSave.classList.remove("disabled");1660exportSave.onclick = function() {1661var files = replaceInFiles(saveArrToFiles(saveObjToArr(saveObj)), "ROMNAME", romName);1662if (!files.length) {1663alert("No save to export.");1664} else if (files.length == 1) {1665downloadFile(files[0].data, "game-sram-" + romName + "-" + getTime() + "." + files[0].path.split(".").slice(1).join("."));1666} else {1667zipFiles(files, function(zd) {1668downloadFile(zd, "game-sram-" + romName + "-" + getTime() + ".zip", "application/zip");1669});1670}1671}16721673// start autosave loop1674autosave.removeAttribute("disabled");1675autosave.parentElement.parentElement.classList.remove("disabled");1676window.setTimeout(function() {1677autosaveSRAM();1678}, 300000);16791680// toggle between sharp and smooth canvas graphics1681smooth.removeAttribute("disabled");1682smooth.parentElement.parentElement.classList.remove("disabled");1683smooth.onclick = function() {1684if (this.checked) {1685canvas.className = "textureSmooth";1686} else {1687canvas.className = "texturePixelated";1688}1689}16901691// pause and resume1692pause.classList.remove("disabled");1693pause.onclick = function() {1694if (this.textContent.trim() == "Pause") {1695Module.pauseMainLoop();1696isPaused = true;1697this.textContent = "Resume";1698document.body.classList.add("paused");1699} else {1700Module.resumeMainLoop();1701isPaused = false;1702this.textContent = "Pause";1703document.body.classList.remove("paused");1704}1705}1706resumeOverlay.onclick = function() {1707pause.click();1708}17091710// toggle menu1711menuButton.classList.remove("disabled");1712menuButton.onclick = function() {1713Module._cmd_toggle_menu();1714}17151716// reset1717resetButton.classList.remove("disabled");1718resetButton.onclick = function() {1719Module._cmd_reset();1720}1721resetButton2.classList.remove("disabled");1722resetButton2.onclick = function() {1723Module._cmd_reset();1724}17251726// toggle mouse grab1727mouseGrabButton.classList.remove("disabled");1728mouseGrabButton.onclick = function(e) {1729e.target.parentElement.style.display = "none";1730Module._cmd_toggle_grab_mouse();1731window.setTimeout(function() {1732canvas.focus();1733canvas.requestPointerLock();1734e.target.parentElement.style.display = "";1735}, 20);1736}17371738// toggle game focus1739gameFocusButton.classList.remove("disabled");1740gameFocusButton.onclick = function(e) {1741e.target.parentElement.style.display = "none";1742Module._cmd_toggle_game_focus();1743window.setTimeout(function() {1744canvas.focus();1745canvas.requestPointerLock();1746e.target.parentElement.style.display = "";1747}, 20);1748}17491750// screenshot button1751takeScreenshot.classList.remove("disabled");1752takeScreenshot.onclick = function() {1753Module._cmd_take_screenshot();1754}17551756// ctrl+v inside canvas1757document.addEventListener("keydown", function(e) {1758if (e.ctrlKey && e.code == "KeyV") {1759fakeKeyPress({code: "Backspace"});1760navigator.clipboard.readText().then(function(text) {1761sendText(text);1762});1763}1764}, false);1765}17661767// start1768function initFromData(data) {1769window.onbeforeunload = function() { return true; }1770async function waitForReady() {1771if (wasmReady && bundleReady && biosReady) {1772setStatus("Waiting for emulator");1773log(data.length == 1 ? "Initializing with " + bytesToHumanReadable(data[0].data.byteLength) + " of data" : "Initializing with multiple files");1774updateNotice.style.display = "none";1775canvas.addEventListener("contextmenu", function(e) {1776e.preventDefault();1777}, false);1778adjustCanvasSize();17791780// prevent defaults for key presses1781document.addEventListener("keydown", function(e) {1782if (pdKeys.includes(e.which)) e.preventDefault();1783}, false);17841785// fix for iframe bug1786if (window.self != window.top) {1787canvas.addEventListener("mousedown", function() {1788window.focus();1789}, false);1790if (!queries.hasOwnProperty("noautorefocus")) {1791window.addEventListener("blur", function(e) {1792window.setTimeout(function() {1793window.focus();1794}, 0);1795}, false);1796}1797}17981799// create the rom(s) in the filesystem1800if (data.length == 1) {1801// single-rom mode18021803realRomExt = data[0].path.split(".").slice(-1)[0] || "bin";1804FS.createPath("/", "rom", true, true);1805FS.writeFile("/rom/rom." + realRomExt, new Uint8Array(data[0].data));1806Module.arguments[0] = "/rom/rom." + realRomExt;1807} else {1808// multi-rom mode18091810var masterIndex = await getMasterRom(data);18111812// now set the romName for multi-file roms1813romName = data[masterIndex].path.split("/").slice(-1)[0].split(".")[0];1814document.title = romName + (appIsPwa ? "" : " | webretro");18151816realRomExt = data[masterIndex].path.split(".").slice(-1)[0] || "bin";1817data[masterIndex].path = "rom." + realRomExt;1818Module.arguments[0] = "/rom/" + data[masterIndex].path;18191820// optionally rename any direct dependencies to "rom"1821if (exclusiveMultiFileCores.includes(core) && confirm('Rename similar files? (Use if you get "Unable to find rom" errors. Otherwise don\'t use.)')) {1822for (var i = 0; i < data.length; i++) {1823if (!data[i].path.includes("/")) data[i].path = data[i].path.replace(romName, "rom");1824}1825}18261827FS.createPath("/", "rom", true, true);1828var parentDirs = Array.from(new Set(data.map(i => i.path.split("/").slice(0, -1).join("/")))).filter(i => i);18291830// create directories1831for (var i = 0; i < parentDirs.length; i++) {1832FS.createPath("/rom/", parentDirs[i], true, true);1833}18341835// create files1836for (var i = 0; i < data.length; i++) {1837FS.writeFile("/rom/" + data[i].path, new Uint8Array(data[i].data));1838}1839}18401841// load save1842var cSave = await getIdbItem("RetroArch_saves_" + romName);1843if (cSave) {1844saveObj = saveArrToObj(cSave);1845FS.createPath("/", baseFsSaveDir.substring(1), true, true);1846for (var i = 0; i < cSave.length; i++) {1847safeWriteFile(baseFsSaveDir + cSave[i].dir + "rom" + cSave[i].ext, cSave[i].data);1848}1849new sideAlert("Save loaded for " + romName, 5000);1850log("Save loaded for " + romName);1851}18521853// import state1854var cState = await getIdbItem("RetroArch_states_" + romName);1855if (cState) {1856FS.createPath("/", "home/web_user/retroarch/userdata/states", true, true);1857FS.writeFile("/home/web_user/retroarch/userdata/states/rom.state", cState);1858new sideAlert("State imported for " + romName + " (press load state)", 5000);1859log("State imported for " + romName);1860}18611862// config1863safeWriteFile("/home/web_user/retroarch/userdata/retroarch.cfg", nulKeys + configObjToStr(savedKeybindsObj) + extraConfig);18641865// get the core options1866var coreOptionsString = "";1867if (coreOptions[core]) {1868pso.style.display = "none";1869try {1870var opts = pso.querySelectorAll("[data-core=" + core + "] input");1871for (var i = 0; i < opts.length; i++) {1872if (opts[i].checked && coreOptions[core][opts[i].dataset.opt]) coreOptionsString += coreOptions[core][opts[i].dataset.opt];1873}1874} catch (e) {1875console.warn(e);1876}1877}18781879// core-specific config (will be revised in the future)1880switch (core) {1881case "a5200":1882safeWriteFile(baseFsConfigDir + "a5200/a5200.opt", coreOptionsString);1883break;1884case "mednafen_psx":1885safeWriteFile(baseFsConfigDir + "Beetle PSX/Beetle PSX.opt", coreOptionsString);1886break;1887case "mednafen_psx_hw":1888safeWriteFile(baseFsConfigDir + "Beetle PSX HW/Beetle PSX HW.opt", coreOptionsString);1889break;1890case "mednafen_vb":1891safeWriteFile(baseFsConfigDir + "Beetle VB/Beetle VB.opt", coreOptionsString);1892break;1893case "mednafen_wswan":1894safeWriteFile(baseFsConfigDir + "Beetle WonderSwan/Beetle WonderSwan.opt", coreOptionsString);1895break;1896case "melonds":1897safeWriteFile(baseFsConfigDir + "melonDS/melonDS.opt", coreOptionsString + 'melonds_touch_mode = "Touch"\n');1898break;1899case "mgba":1900safeWriteFile(baseFsConfigDir + "mGBA/mGBA.opt", coreOptionsString);1901break;1902case "mupen64plus_next":1903safeWriteFile(baseFsConfigDir + "Mupen64Plus-Next/Mupen64Plus-Next.opt", coreOptionsString + 'mupen64plus-ThreadedRenderer = "False"\nmupen64plus-EnableCopyColorToRDRAM = "Off"\nmupen64plus-EnableCopyDepthToRDRAM = "Off"\n');1904break;1905case "o2em":1906safeWriteFile(baseFsConfigDir + "O2EM/O2EM.opt", coreOptionsString);1907break;1908case "parallel_n64":1909safeWriteFile(baseFsConfigDir + "ParaLLEl N64/ParaLLEl N64.opt", coreOptionsString);1910break;1911case "prosystem":1912safeWriteFile(baseFsConfigDir + "ProSystem/ProSystem.opt", coreOptionsString);1913break;1914case "snes9x": // actually a remap1915safeWriteFile(baseFsConfigDir + "remaps/Snes9x/Snes9x.rmp", coreOptionsString);1916break;1917case "stella2014":1918safeWriteFile(baseFsConfigDir + "Stella 2014/Stella 2014.opt", coreOptionsString);1919break;1920case "vecx":1921safeWriteFile(baseFsConfigDir + "VecX/VecX.opt", coreOptionsString);1922break;1923case "virtualjaguar":1924safeWriteFile(baseFsConfigDir + "Virtual Jaguar/Virtual Jaguar.opt", coreOptionsString + 'virtualjaguar_bios = "enabled"\n');1925break;1926case "yabause":1927safeWriteFile(baseFsConfigDir + "Yabause/Yabause.opt", coreOptionsString);1928break;1929}19301931// system-specific config1932switch (systems[core]) {1933case "SNES":1934var hash = md5(u8atoutf8(new Uint8Array(data[0].data)));1935if (smasBrickFix.hasOwnProperty(hash)) {1936FS.writeFile("/rom/rom.ips", new Uint8Array(smasBrickFix[hash]));1937new sideAlert("SMAS Bricks Fixed!", 5000);1938}1939break;1940}19411942// writeToFile tracking (needs some extra stuff since it frequently fires in groups)1943FS.trackingDelegate.onWriteToFile = function(path) {1944if (!path.startsWith("/dev/")) {1945if (writeToFileCooldown[path]) window.clearTimeout(writeToFileCooldown[path]);1946writeToFileCooldown[path] = window.setTimeout(function() {1947delete writeToFileCooldown[path];1948FSTracking.dispatchEvent(new CustomEvent("writeToFile", {detail: path}));19491950// bigger delay = more lenient1951}, 1000);1952}1953}19541955FSTracking.addEventListener("writeToFile", function(e) {1956writeToFileHandler(e.detail);1957}, false);19581959// start1960log("Calling main...");1961try {1962Module.callMain(Module.arguments);1963} catch (e) {1964var estr = "FAILED TO CALL MAIN. CHECK BROWSER CONSOLE FOR DETAILS. (core: " + core + ")";1965alert(estr);1966log(estr);1967console.error(e);1968}1969log("Main completed...");19701971adjustCanvasSize();1972loadingDiv.style.display = "none";19731974window.setTimeout(afterStart, 1000);1975} else {1976window.setTimeout(waitForReady, 250);1977}1978}1979waitForReady();1980}19811982var Module = {1983canvas: canvas,1984noInitialRun: true,1985arguments: ["/rom/rom.bin", "--verbose"],1986onRuntimeInitialized: function() {1987wasmReady = true;1988log("WASM ready");19891990// fetch BIOSes1991prepareBios();19921993// fetch asset bundle1994if (queries.hasOwnProperty("nobundle")) {1995bundleReady = true;1996log("Skipping bundle");1997} else {1998prepareBundle();1999}2000},2001print: function(text) {2002log("stdout: " + text);2003},2004printErr: function(text) {2005log("stderr: " + text);2006}2007};200820092010