NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Retro Enhanced // @namespace https://github.com/miagui // @version 1.4.3 // @description Additional functionality for retro gaming related sites. // @author Miagui // @match *://retroachievements.org/* // @license MIT // @updateURL https://openuserjs.org/meta/Miagui/Retro_Enhanced.meta.js // @downloadURL https://openuserjs.org/install/Miagui/Retro_Enhanced.user.js // @grant GM_xmlhttpRequest // @grant GM_log // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @connect archive.org // @connect the-eye.eu // @connect raw.githubusercontent.com // @connect sheets.googleapis.com // @connect emuparadise.meg // @connect speedrun.com // @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js // ==/UserScript== (async function () { var pageWithParams = (location.pathname + location.search).substr(1); var page = location.pathname; var loggedUser = $(".brand-top strong > a").text(); var sectionName = window.location.hash.slice(1); // Configs var enableSpeedrun = await GM_getValue("enableSpeedrun", false); var enableRomSearch = await GM_getValue("enableRomSearch", true); var enableCustomBG = await GM_getValue("enableCustomBG", true); var enableGameplayVideo = await GM_getValue("enableGameplayVideo", true); var enableEmuparadise = await GM_getValue("enableEmuparadise", false); var prioritizeEmuparadise = await GM_getValue("prioritizeEmuparadise", false); var enableGlassEffect = await await GM_getValue("enableGlassEffect", true); var RAConsole = { ARCADE: "Arcade", SNES: "SNES/Super Famicom", NES: "NES/Famicom", GAMEBOY: "Game Boy", GBC: "Game Boy Color", GBA: "Game Boy Advance", N64: "Nintendo 64", GCN: "GameCube", NDS: "Nintendo DS", A2600: "Atari 2600", A7800: "Atari 7800", PCENGINE: "PC Engine/TurboGrafx-16", PCENGINECD: "PC Engine CD/TurboGrafx-CD", MASTERSYSTEM: "Master System", GG: "Game Gear", GENESIS: "Mega Drive", SEGA32X: "32X", SEGACD: "Sega CD", SATURN: "Saturn", DREAMCAST: "Dreamcast", PS1: "PlayStation", PS2: "PlayStation 2", PSP: "PlayStation Portable", PANASONIC3DO: "3DO Interactive Multiplayer", NEOGEOCD: "Neo Geo CD", NEOPOCKET: "Neo Geo Pocket", POKEMINI: "Pokemon Mini", VIRTUALBOY: "Virtual Boy", SG1000: "SG-1000", COLECO: "ColecoVision", MSX: "MSX", WONDERSWAN: "WonderSwan", VECTREX: "Vectrex", NEC8800: "PC-8000/8800", APPLEII: "Apple II" }; var SRConsole = { PC: "8gej2n93", APPLEII: "w89ryw6l", ATARI2600: "o0644863", ARCADE: "vm9vn63k", NEC8800: "7g6mw8er", COLECOVISION: "wxeo8d6r", COMMODORE64: "gz9qox60", MSX: "jm950z6o", NES: "jm95z9ol", MSX2: "83exkk6l", MASTERSYSTEM: "83exwk6l", ATARI7800: "gde33gek", FAMICOMDISKSYSTEM: "mr6k409z", PCENGINE: "5negxk6y", GENESIS: "mr6k0ezw", GAMEBOY: "n5683oev", NEOGEOAES: "mx6p4w63", GG: "w89r3w9l", SNES: "83exk6l5", PHILIPSCDI: "w89rjw6l", SEGACD: "31670d9q", PANASONIC3D0: "8gejmne3", NEOGEOCD: "kz9w7mep", PCFX: "p36n8568", PS1: "wxeod9rn", SEGA32X: "kz9wrn6p", SEGASATURN: "lq60l642", VIRTUALBOY: "7g6mk8er", N64: "w89rwelk", GAMEBOYCOLOR: "gde3g9k1", NEOGEOPOCKETCOLOR: "7m6ydw6p", TURBOGRAFX16CD: "p36nlxe8", DREAMCAST: "v06d394z", WONDERSWAN: "vm9v8n63", PLAYSTATION2: "n5e17e27", WONDERSWANCOLOUR: "n568kz6v", GAMEBOYADVANCE: "3167d6q2", GAMECUBE: "4p9z06rn", POKÉMONMINI: "vm9vr1e3", NDS: "7g6m8erk", PLAYSTATIONPORTABLE: "5negk9y7", WII: "v06dk3e4" } // GM_log(enableSpeedrun) // GM_log(enableRomSearch) // GM_log(enableCustomBG) // GM_log(page) // GM_log(sectionName) if (page == "/controlpanel.php") { $("article > .detaillist > .component:eq(1)").after("<div id='enhanced-settings' class='component'></div>") let settingsDiv = $("#enhanced-settings"); settingsDiv.append("<h3>Retro Enhanced</h3>") settingsDiv.append(`<table> <tbody> <tr> <td>Enable ROMs search:</td> <td><input id="enhanced-romsearch" type="checkbox" ${enableRomSearch ? "checked='checked'" : ""}></td> </tr> <tr> <td>Add Emuparadise to ROMs search: <div style="font-size: 0.8em;">(For Chrome users: <a href="https://experienceleague.adobe.com/docs/target/using/experiences/vec/troubleshoot-composer/mixed-content.html?lang=en#task_FF297A08F66E47A588C14FD67C037B3A">enable this</a>; For all browsers: must click on "Add Exception and Get my File!" for the first time)</div></td> <td><input id="enhanced-epromsearch" type="checkbox" ${enableEmuparadise ? "checked='checked'" : ""}></td> </tr> <tr> <td>Prioritize Emuparadise for ROMs search (must have "Add Emuparadise to ROMs search" enabled):</td> <td><input id="enhanced-prioritize_ep" type="checkbox" ${prioritizeEmuparadise ? "checked='checked'" : ""}></td> </tr> <tr> <td>Enable Speedrun.com stats:</td> <td><input id="enhanced-speedrun" type="checkbox" ${enableSpeedrun ? "checked='checked'" : ""}></td> </tr> <tr> <td>Enable gameplay video on the game page:</td> <td><input id="enhanced-gameplayvideo" type="checkbox" ${enableGameplayVideo ? "checked='checked'" : ""}></td> </tr> <tr> <td>Enable custom game page background:</td> <td><input id="enhanced-custombg" type="checkbox" ${enableCustomBG ? "checked='checked'" : ""}></td> </tr> <tr> <td>Enable glass background effect:</td> <td><input id="enhanced-glassEffect" type="checkbox" ${enableGlassEffect ? "checked='checked'" : ""}></td> </tr> </tbody> </table>`) $(document).on('change', '#enhanced-romsearch', function () { GM_setValue("enableRomSearch", $("#enhanced-romsearch").is(":checked")) }) $(document).on('change', '#enhanced-epromsearch', function () { GM_setValue("enableEmuparadise", $("#enhanced-epromsearch").is(":checked")) }) $(document).on('change', '#enhanced-prioritize_ep', function () { GM_setValue("prioritizeEmuparadise", $("#enhanced-prioritize_ep").is(":checked")) }) $(document).on('change', '#enhanced-speedrun', function () { GM_setValue("enableSpeedrun", $("#enhanced-speedrun").is(":checked")) }) $(document).on('change', '#enhanced-custombg', function () { GM_setValue("enableCustomBG", $("#enhanced-custombg").is(":checked")) }) $(document).on('change', '#enhanced-gameplayvideo', function () { GM_setValue("enableGameplayVideo", $("#enhanced-gameplayvideo").is(":checked")) }) $(document).on('change', '#enhanced-glassEffect', function () { GM_setValue("enableGlassEffect", $("#enhanced-glassEffect").is(":checked")) }) } // ========================================= // Game Set Page // ========================================= // Regex: game/13918 else if (page.match(/game\/[0-9]/g) != null || page == "/gameInfo.php") { // General var console = $(".navpath").children().eq(1).text(); // var game = $(".navpath").children().eq(2).text(); // var gameId = /([^\/]+)\/?$/g.exec($("meta[property='og:url']").attr("content"))[0]; // var points = $("#achievement .TrueRatio").eq(0).prev().text(); var icon = $("#achievement div:eq(2) > img").attr("src") var tag = ""; var gameImg = $("#achievement img[alt=\"In-game screenshot\"]").attr("src"); var rgxTag = /\~(.*?)\~/g; // GM_log("Icon:" + icon) // Check for tags if (game.match(rgxTag) != undefined) { tag = rgxTag.exec(game)[1]; game = game.replace(game.match(rgxTag) + " ", ""); } // GM_log(gameId) var isAvailable = false; var collection = { name: "", url: "" }; var results = []; var resultsDlcs = []; // Speedrun.com API resources var srRoot = "https://www.speedrun.com/api/v1/"; var srLogo = ""; var srVideoUrl = ""; var srGamelink = ""; var srGameId = ""; var srRuns = []; // GM_log(game) // GM_log(console) // Avoid unwanted exceptions for hubs pages. if (console == "") return // ========================================= // Divs Insertion // ========================================= // Set custom background if (gameImg != "https://media.retroachievements.org/Images/000002.png" && enableCustomBG) { $("body").append(`<style> body:before { content: ""; position: fixed; width : 110%; height: 110%; background: inherit; background-size: cover; z-index: -1; overflow: hidden; filter : blur(8px); -moz-filter : blur(8px); -webkit-filter: blur(8px); -o-filter : blur(8px); } </style>`); $("body").css({ "background-image": `url(${gameImg})`, "background-attachment": "fixed", "background-size": "90%", "background-position": "center", "background-repeat": "no-repeat" }) } // Change to glass background if (enableGlassEffect) { $(":root").attr("style", "--box-bg-color: rgba(35, 35, 35, 0.95);") $("#leftcontainer").attr("style", "background: var(--box-bg-color);") $("#rightcontainer").attr("style", "background: var(--box-bg-color);") } // Change top nav to body color $(".brand-top").addClass("bg-body") // Change header background to transparent // $(".brand-top + nav").removeClass("bg-body") // Prepare divs $("aside > .gamealts").first().before("<div id='romsdl'></div>") $("aside > .gamealts").first().before("<div id='speedruncom'></div>") var divRoms = $("#romsdl"); var divSpeedruncom = $("#speedruncom"); divRoms.css("margin-top", "1em") divSpeedruncom.css("margin", "1em 0em"); if (enableGameplayVideo || enableSpeedrun) getSpeedruns(game) // ========================================= // Rom Search // ========================================= if (enableRomSearch) { // Verify if system is available for (const prop in RAConsole) { if (RAConsole.hasOwnProperty(prop)) { const element = RAConsole[prop]; if (element == console) isAvailable = true; } } if ((isAvailable && tag == "") || (console == RAConsole.ARCADE && tag != "")) { var promise; // Deprecated feature to find roms for hacks and homebrews. // if (tag != "" && console != RAConsole.ARCADE) { // promise = searchCustom(); // collection.name = "Custom"; // } // Search through Emuparadise if (enableEmuparadise && prioritizeEmuparadise) { collection.name = "Emuparadise"; collection.url = "https://www.emuparadise.me/roms-isos-games.php" promise = searchEmuparadise(); } else { // resolve the promise so the function don't stop working promise = Promise.resolve() } // Search through archives.org promise.then(() => { if (results.length == 0) { if (console == RAConsole.NDS) { collection.name = "No-Intro Nintendo DS Decrypted"; collection.url = "https://archive.org/download/noIntroNintendoDsDecrypted2019Jun30" return searchArchive("https://archive.org/download/noIntroNintendoDsDecrypted2019Jun30"); } else if (console == RAConsole.PS1) { collection.name = "[REDUMP] Disc Image Collection: Sony - Sony PlayStation"; collection.url = "https://archive.org/details/redump.psx" return searchArchive("https://archive.org/download/redump.psx") .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/redump.psx.p2"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/redump.psx.p3"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/redump.psx.p4"); else return; }); } else if (console == RAConsole.PS2) { collection.name = "PS2 Redump USA CHD"; collection.url = "https://archive.org/details/ps2-redump-usa-chd-part-A" // PS2 Redump USA CHD is separated per alphabet, so create a link from it instead of checking each page let ps2link = "https://archive.org/download/ps2-redump-usa-chd-part-" + simplify_title(game).charAt(0).toUpperCase() return searchArchive(ps2link) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/ps2chd1"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/ps2chd2"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/ps2chd3"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/ps2chd4"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/ps2chd5"); else return; }) } else if (console == RAConsole.PSP) { collection.name = "[REDUMP] Disc Image Collection: Sony PlayStation Portable" collection.url = "https://archive.org/download/redump.psp" return searchArchive("https://archive.org/download/redump.psp") .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/redump.psp.p2"); else return; }) .then(() => { if (results.length == 0) { collection.name = "PSN Collection By Ghostware" collection.url = "https://archive.org/download/PSNCollectionByGhostware" return searchArchive("https://archive.org/download/PSNCollectionByGhostware"); } else return; }) } else if (console == RAConsole.SATURN) { collection.name = "Redump Sega Saturn 2018"; collection.url = "https://archive.org/download/SegaSaturn2018July10" return searchArchive("https://archive.org/download/SegaSaturn2018July10"); } else if (console == RAConsole.DREAMCAST) { collection.name = "[REDUMP] Disc Image Collection: Sega - Sega Dreamcast"; collection.url = "https://archive.org/download/redump.dc.revival"; return searchArchive("https://archive.org/download/redump.dc.revival"); } else if (console == RAConsole.SEGACD) { collection.name = "Redump Sega Mega CD & Sega CD"; collection.url = "https://archive.org/download/redump.sega_megacd-segacd" return searchArchive("https://archive.org/download/redump.sega_megacd-segacd"); } else if (console == RAConsole.NEOGEOCD) { collection.name = "[REDUMP] Disc Image Collection: SNK - Neo Geo CD"; collection.url = "https://archive.org/download/redump.ngcd.revival" return searchArchive("https://archive.org/download/redump.ngcd.revival"); } else if (console == RAConsole.ARCADE) { collection.name = "FB Neo Nightly" collection.url = "https://archive.org/download/2020_01_06_fbn" return searchArcade(); } else if (console == RAConsole.A2600) { collection.name = "No-Intro Atari 2600" collection.url = "https://archive.org/download/nointro2600atarii" return searchArchive("https://archive.org/download/nointro2600atarii"); } else if (console == RAConsole.NEC8800) { collection.name = "Neo Kobe NEC PC-8801/8001" collection.url = "https://archive.org/details/Neo_Kobe_NEC_PC-8001_2016-02-25" return searchArchive("https://ia800202.us.archive.org/view_archive.php?archive=/18/items/Neo_Kobe_NEC_PC-8801_2016-02-25/Neo%20Kobe%20-%20NEC%20PC-8801%20%282016-02-25%29.zip") // Search for 8001 if it finds nothing. .then(() => { if (results.length == 0) return searchArchive("https://ia600204.us.archive.org/view_archive.php?archive=/18/items/Neo_Kobe_NEC_PC-8001_2016-02-25/Neo%20Kobe%20-%20NEC%20PC-8001%20%282016-02-25%29.zip)"); else return; }) } else if (console == RAConsole.APPLEII) { collection.name = "Apple 2 TOSEC 2012" collection.url = "https://archive.org/details/Apple_2_TOSEC_2012_04_23" return searchArchive("https://ia802908.us.archive.org/view_archive.php?archive=/25/items/Apple_2_TOSEC_2012_04_23/Apple_2_TOSEC_2012_04_23.zip"); } else { collection.name = "No-Intro 2016" collection.url = "https://archive.org/download/No-Intro-Collection_2016-01-03_Fixed" return searchNoIntro2016(); } // exit if already has results } else return; }) .then(() => { if (enableEmuparadise && results.length == 0) { collection.name = "Emuparadise"; collection.url = "https://www.emuparadise.me/roms-isos-games.php" return searchEmuparadise(); } }) .then(() => { if (console == RAConsole.PSP) return searchArchiveDlc("https://archive.org/download/PSP-DLC/%5BNo-Intro%5D%20PSP%20DLC/") }) .then(() => { // GM_log(results); if (results.length > 0) createDownloads() if (resultsDlcs.length > 0) createDlcs() }) } else { // divRoms.append("<b>Searching roms for this system not supported.</b>"); GM_log("Searching roms for this system not supported.") } } // ========================================= // Create Content to the Page // ========================================= function createDownloads() { divRoms.append("<h3>ROMs</h3>"); // divRoms.append(`<b>Found ${results.length} related result(s)`) divRoms.append(``); for (var i = 0; i < results.length; i++) { let dlLink = (results[i].url).replace(/ /g, "%20"); divRoms.append("<a class='dl-links' href=" + dlLink + ">" + removeExt(results[i].name) + "</a>"); } if (collection.url != "") divRoms.append(`<div style="margin-top:1em;">From <a href=${collection.url}>${collection.name}</a></div>`); $(".dl-links").css("display", "block"); } function createDlcs() { divRoms.append(`<h3 style="margin-top: 1em;">DLCs</h3>`); //divRoms.append(`<b>Found ${resultsDlcs.length} related result(s)`) divRoms.append(``); for (var i = 0; i < resultsDlcs.length; i++) { let dlLink = (resultsDlcs[i].url).replace(/ /g, "%20"); divRoms.append("<a class='dl-links' href=" + dlLink + ">" + removeExt(resultsDlcs[i].name) + "</a>"); } if (collection.url != "") divRoms.append(`<div style="margin-top:1em;">From <a href=https://archive.org/download/PSP-DLC/%5BNo-Intro%5D%20PSP%20DLC>PSP-DLC (No-Intro)</a></div>`); $(".dl-links").css("display", "block"); } function createSpeedrun() { divSpeedruncom.append("<h3>World Records</h3>"); // divSpeedruncom.append(`<a href=${srGamelink}> // <img style="display: block; width: 100%; object-fit: cover; margin: 1em 0em 1em 0em" // src=${srLogo}></img> // </a>`) // GM_log(srRuns) // GM_log(srRuns.length) if (srRuns.length > 0) { srRuns.forEach((runsData) => { // GM_log(runsData.link) divSpeedruncom.append(`<div><a href=${runsData.link}>${runsData.category}:</a> ${runsData.time} by ${runsData.runner}</div>`) }); } else { divSpeedruncom.append("<div>Couldn't find this game on Speedrun.com</div>") } } function createVideo() { if (srVideoUrl != "") GM_log("Creating video with URL: " + srVideoUrl) $("#achievement").children().eq(3) .after($(`<iframe style="display: block; width: 100%; height:315px; padding-bottom: 1em" src="${srVideoUrl}" allowfullscreen="allowfullscreen" autoplay="false"> </iframe>`)); } function getSrConsoleId(consoleName) { if (consoleName == RAConsole.A2600) { return SRConsole.ATARI2600; } else if (consoleName == RAConsole.A7800) { return SRConsole.ATARI7800; } else if (consoleName == RAConsole.APPLEII) { return SRConsole.APPLEII; } else if (consoleName == RAConsole.ARCADE) { return SRConsole.ARCADE; } else if (consoleName == RAConsole.COLECO) { return SRConsole.COLECOVISION; } else if (consoleName == RAConsole.DREAMCAST) { return SRConsole.DREAMCAST; } else if (consoleName == RAConsole.GAMEBOY) { return SRConsole.GAMEBOY; } else if (consoleName == RAConsole.GBA) { return SRConsole.GAMEBOYADVANCE; } else if (consoleName == RAConsole.GBC) { return SRConsole.GAMEBOYCOLOR; } else if (consoleName == RAConsole.GENESIS) { return SRConsole.GENESIS; } else if (consoleName == RAConsole.GG) { return SRConsole.GG; } else if (consoleName == RAConsole.N64) { return SRConsole.N64; } else if (consoleName == RAConsole.SATURN) { return SRConsole.SEGASATURN; } else if (consoleName == RAConsole.MASTERSYSTEM) { return SRConsole.MASTERSYSTEM; } else if (consoleName == RAConsole.NDS) { return SRConsole.NDS; } else if (consoleName == RAConsole.NEC8800) { return SRConsole.NEC8800; } else if (consoleName == RAConsole.NEOPOCKET) { return SRConsole.NEOGEOPOCKETCOLOR; } else if (consoleName == RAConsole.NES) { return SRConsole.NES; } else if (consoleName == RAConsole.PANASONIC3DO) { return SRConsole.PANASONIC3D0; } else if (consoleName == RAConsole.PCENGINE) { return SRConsole.PCENGINE; } else if (consoleName == RAConsole.POKEMINI) { return SRConsole.POKÉMONMINI; } else if (consoleName == RAConsole.PS1) { return SRConsole.PS1; } else if (consoleName == RAConsole.PSP) { return SRConsole.PLAYSTATIONPORTABLE } else if (consoleName == RAConsole.SEGA32X) { return SRConsole.SEGA32X; } else if (consoleName == RAConsole.SEGACD) { return SRConsole.SEGACD; } else if (consoleName == RAConsole.SG1000) { return SRConsole.MASTERSYSTEM; } else if (consoleName == RAConsole.SNES) { return SRConsole.SNES; } else if (consoleName == RAConsole.VIRTUALBOY) { return SRConsole.VIRTUALBOY; } else return ""; } function getSpeedruns(gameName) { var consoleId = getSrConsoleId(console); var srSearchUrl = encodeURI(srRoot + "games?name=" + gameName + "&platform=" + consoleId) // Games // GM_log(srSearchUrl) return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: srSearchUrl, onload: function (gamesResponse) { let gamesData = JSON.parse(gamesResponse.response).data // GM_log("Games: " + gamesData) if (gamesData.length > 0) { srGamelink = gamesData[0].weblink; srGameId = gamesData[0].id; // srLogo = gamesData[0].assets.logo.uri; don't work as of now, possibly due to API changes on Speedrun.com resolve(gamesData[0].links[3].uri); } else { throw `Couldn't find this game on Speedrun.com (${srSearchUrl}).`; } } }) }) .then((link) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: link, onload: function (response) { let categoriesData = JSON.parse(response.response).data resolve(categoriesData) } }) }) }, err => { throw (err) }) .then((categories) => { // GM_log("Categories: " + categories) let totalRuns = 0; return Promise.all(categories.map(category => { // Runs return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: srRoot + `runs?game=${srGameId}&category=${category.id}&status=verified`, onload: function (runsResponse) { let runsData = JSON.parse(runsResponse.response).data[0]; if (runsData != undefined && runsData.status.status != "rejected") { // Pick a eligible speedrun video and use it as the video showcase on the page // GM_log(runsData.videos) if (srVideoUrl == "" && runsData.videos) srVideoUrl = toEmbedUrl(runsData.videos.links[0].uri); // GM_log(isVideoUrl(runsData.videos.links[0].uri)) // if (isVideoUrl(runsData.videos.links[0].uri)) totalRuns++; } resolve(runsData) } }) }) .then((runsData) => { if (runsData == undefined) return false; // TODO: Resolve useless request if user is a guest. let isGuest = runsData.players[0].rel == "guest" ? true : false; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: srRoot + "users/" + runsData.players[0].id, onload: function (userRes) { userData = JSON.parse(userRes.response).data srRuns.push({ category: category.name, time: parseIso8601(runsData.times.primary), runner: isGuest ? runsData.players[0].name : userData.names.international, link: runsData.videos ? runsData.videos.links[0].uri : "" }) // GM_log(srRuns.length) // GM_log(totalRuns) resolve(true) } }) }) }) })) .then(() => { // GM_log("Last step from promises") // GM_log(srRuns) if (enableSpeedrun) createSpeedrun(); if (enableGameplayVideo) createVideo(); }) }) } // Functional // return $.get(srSearchUrl) // .then((gamesResponse) => { // if (gamesResponse.data.length > 0) { // srGamelink = gamesResponse.data[0].weblink; // srGameId = gamesResponse.data[0].id; // srLogo = gamesResponse.data[0].assets.logo.uri; // return gamesResponse.data[0].links[3].uri; // } else { // throw `Couldn't find this game on Speedrun.com (${srSearchUrl}).`; // } // }, err => { // throw (err) // }) // // Categories // .then((link) => { // return $.get(link).then(response => response.data) // }, err => { // throw (err) // }) // .then((categories) => { // // GM_log(categories) // let totalRuns = 0; // return Promise.all(categories.map(category => { // // Runs // return $.get(srRoot + `runs?game=${srGameId}&category=${category.id}&status=verified`) // .then((runsResponse) => { // let runsData = runsResponse.data[0]; // if (runsData != undefined && runsData.status.status != "rejected") { // // GM_log(runsData.videos) // if (srVideoUrl == "" && runsData.videos) // srVideoUrl = toEmbedUrl(runsData.videos.links[0].uri); // totalRuns++; // return runsData; // } // }) // .then((runsData) => { // if (runsData == undefined) return false; // // TODO: Resolve useless request if user is a guest. // let isGuest = runsData.players[0].rel == "guest" ? true : false; // return $.get(srRoot + "users/" + runsData.players[0].id) // .then(userRes => { // srRuns.push({ // category: category.name, // time: parseIso8601(runsData.times.primary), // runner: isGuest ? runsData.players[0].name : userRes.data.names.international, // link: runsData.videos ? runsData.videos.links[0].uri : "" // }) // // GM_log(srRuns.length) // // GM_log(totalRuns) // return true; // }) // }) // })) // .then(() => { // if (enableSpeedrun) createSpeedrun(); // if (enableGameplayVideo) createVideo(); // }) // }, err => { // throw (err) // }) // } // Broken?? // return $.get(srSearchUrl) // .then((gamesResponse) => { // if (gamesResponse.data.length > 0) { // srGamelink = gamesResponse.data[0].weblink; // srGameId = gamesResponse.data[0].id; // srLogo = gamesResponse.data[0].assets.logo.uri; // return gamesResponse.data[0].links[3].uri; // } else { // throw `Couldn't find this game on Speedrun.com (${srSearchUrl}).`; // } // }, err => { // throw (err) // }) // // Categories // .then((link) => { // return $.get(link).then(response => response.data) // }, err => { // throw (err) // }) // .then((categories) => { // // GM_log(categories) // let totalRuns = 0; // return Promise.all(categories.map(category => { // // Runs // return $.get(srRoot + `runs?game=${srGameId}&category=${category.id}&status=verified`) // .then((runsResponse) => { // let runsData = runsResponse.data[0]; // if (runsData != undefined && runsData.status.status != "rejected") { // // GM_log(runsData.videos) // if (srVideoUrl == "" && runsData.videos) // srVideoUrl = toEmbedUrl(runsData.videos.links[0].uri); // totalRuns++; // return runsData; // } // }) // .then((runsData) => { // if (runsData == undefined) return false; // // TODO: Resolve useless request if user is a guest. // let isGuest = runsData.players[0].rel == "guest" ? true : false; // return $.get(srRoot + "users/" + runsData.players[0].id) // .then(userRes => { // srRuns.push({ // category: category.name, // time: parseIso8601(runsData.times.primary), // runner: isGuest ? runsData.players[0].name : userRes.data.names.international, // link: runsData.videos ? runsData.videos.links[0].uri : "" // }) // // GM_log(srRuns.length) // // GM_log(totalRuns) // return true; // }) // }) // })) // .then(() => { // if (enableSpeedrun) createSpeedrun(); // if (enableGameplayVideo) createVideo(); // }) // }, err => { // throw (err) // }) // } // function searchCustom() { // var spreadsheetId = "1IhU4rxn9aZ44HHTttYxTxeGlamQ7KHmFdiAtGjFK8Sg"; // var publicKey = "AIzaSyBX7lt6Dzt4NZti7Du_xxAD_qPKb4xKZ-4"; // return new Promise((resolve, reject) => { // GM_xmlhttpRequest({ // method: "GET", // url: `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${console}!A2:C1000?key=${publicKey}`, // onload: function (response) { // var sheet = JSON.parse(response.responseText); // sheet.values.forEach(row => { // // Stop in case tag is not matching // if (row[0] != tag) return; // if (refinedCompare(row[1], game)) { // // GM_log(row) // results.push({ // name: `${row[1]} (${row[0]})`, // url: row[2] // }); // } // }); // if (results.length == 0) { // sheet.values.forEach(row => { // if (compare(row[1], game)) { // // GM_log(row) // results.push({ // name: `${row[1]} (${tag})`, // url: row[2] // }); // } // }); // } // resolve(true); // } // }) // }) // } // ========================================= // Archives.org (Arcade) Function // ========================================= function searchArcade() { var mainDir = "//archive.org/download/2020_01_06_fbn/roms/arcade.zip/arcade%2F"; var datDir = "https://raw.githubusercontent.com/libretro/FBNeo/master/dats/FinalBurn%20Neo%20(ClrMame%20Pro%20XML%2C%20Arcade%20only).dat"; return new Promise((resolve, reject) => { // Find content in .dat file from FB Neo repo. GM_xmlhttpRequest({ method: "GET", url: datDir, onload: function (response) { // GM_log(response); var xmlDoc = $.parseXML(response.responseText); // Refined search $(xmlDoc).find("game").each(function (index) { let name = $(this).find("description").text(); if (tag == "" && name.toLowerCase().includes("hack")) return; if (tag == "Hack" && !name.toLowerCase().includes("hack")) return; if (refinedCompare(name, game)) { results.push({ name: name, url: mainDir + $(this).attr('name') + ".zip" }); } }) // Normal search if (results.length == 0) { $(xmlDoc).find("game").each(function (index) { let name = $(this).find("description").text(); if (tag == "" && name.toLowerCase().includes("hack")) return; if (tag == "Hack" && !name.toLowerCase().includes("hack")) return; if (compare(name, game)) { results.push({ name: name, url: mainDir + $(this).attr('name') + ".zip" }); } }) } resolve(true); } }) }); } // ========================================= // Archives.org (No Intro 2016) Function // ========================================= function searchNoIntro2016() { var mainDir = "https://archive.org/download/No-Intro-Collection_2016-01-03_Fixed/"; var consoleDir = ""; var secondaryConsoleDir = ""; if (console == RAConsole.SNES) { consoleDir = "Nintendo - Super Nintendo Entertainment System"; } if (console == RAConsole.NES) { consoleDir = "Nintendo - Nintendo Entertainment System"; } if (console == RAConsole.GAMEBOY) { consoleDir = "Nintendo - Game Boy"; } if (console == RAConsole.GBC) { consoleDir = "Nintendo - Game Boy Color"; } if (console == RAConsole.GBA) { consoleDir = "Nintendo - Game Boy Advance"; } if (console == RAConsole.N64) { consoleDir = "Nintendo - Nintendo 64"; } if (console == RAConsole.A7800) { consoleDir = "Atari - 7800"; } if (console == RAConsole.PCENGINE) { consoleDir = "NEC - PC Engine - TurboGrafx 16"; } if (console == RAConsole.GENESIS) { consoleDir = "Sega - Mega Drive - Genesis"; } if (console == RAConsole.MASTERSYSTEM) { consoleDir = "Sega - Master System - Mark III"; } if (console == RAConsole.GG) { consoleDir = "Sega - Game Gear"; } if (console == RAConsole.NEOPOCKET) { consoleDir = "SNK - Neo Geo Pocket"; secondaryConsoleDir = "SNK - Neo Geo Pocket Color"; } if (console == RAConsole.POKEMINI) { consoleDir = "Nintendo - Pokemon Mini"; } if (console == RAConsole.VIRTUALBOY) { consoleDir = "Nintendo - Virtual Boy.zip"; } if (console == RAConsole.SG1000) { consoleDir = "Sega - SG-1000"; } if (console == RAConsole.COLECO) { consoleDir = "Coleco - ColecoVision"; } if (console == RAConsole.MSX) { consoleDir = "Microsoft - MSX"; secondaryConsoleDir = "Microsoft - MSX 2"; } if (console == RAConsole.WONDERSWAN) { consoleDir = "Bandai - WonderSwan Color"; secondaryConsoleDir = "Bandai - WonderSwan Color" } if (console == RAConsole.VECTREX) { consoleDir = "GCE - Vectrex"; } consoleDir = consoleDir.replace(/ /g, "%20").concat(".zip/"); secondaryConsoleDir = secondaryConsoleDir.replace(/ /g, "%20").concat(".zip/"); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: mainDir + consoleDir, onload: function (response) { // GM_log(response); // Refined search $(response.responseText).find(`td`).children(":first-child").each(function (index) { if (refinedCompare($(this).text(), game)) { results.push({ name: $(this).text(), url: $(this).attr('href') }); } }); // Normal search if (results.length == 0) { $(response.responseText).find(`td`).children(":first-child").each(function (index) { if (compare($(this).text(), game)) { results.push({ name: $(this).text(), url: $(this).attr('href') }); } }); } resolve(true); } }) }) .then(() => { if (secondaryConsoleDir != "") { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: mainDir + secondaryConsoleDir, onload: function (response) { // GM_log(response); // Refined search $(response.responseText).find(`td`).children(":first-child").each(function (index) { if (refinedCompare($(this).text(), game)) { results.push({ name: $(this).text(), url: $(this).attr('href') }); } }); // Normal search if (results.length == 0) { $(response.responseText).find(`td`).children(":first-child").each(function (index) { if (compare($(this).text(), game)) { results.push({ name: $(this).text(), url: $(this).attr('href') }); } }); } resolve(true); } }) }) } }) } // ========================================= // Archives.org (Generic) Function // ========================================= function searchArchive(mainDir) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: mainDir, onload: function (response) { // GM_log(response.responseText); // Refined search $(response.responseText).find(`td`).children(":first-child").each(function (index) { let title = /([^\/]+)\/?$/g.exec($(this).text())[1]; // Check in case link is already the full url let fullUrl = $(this).attr('href').startsWith("//archive.org/download/") ? $(this).attr('href') : `${mainDir}/${$(this).attr('href')}` // GM_log($(this).attr('href')) if (refinedCompare(title, game)) { results.push({ name: title, url: fullUrl }); } }); // Normal search if (results.length == 0) { $(response.responseText).find(`td`).children(":first-child").each(function (index) { let title = /([^\/]+)\/?$/g.exec($(this).text())[1]; let fullUrl = $(this).attr('href').startsWith("//archive.org/download/") ? $(this).attr('href') : `${mainDir}/${$(this).attr('href')}` // GM_log($(this).attr('href')) if (compare(title, game)) { results.push({ name: title, url: fullUrl }); } }); } return resolve(true); } }) }); } // ========================================= // Archives.org (DLC) Function // ========================================= function searchArchiveDlc(mainDir) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: mainDir, onload: function (response) { // GM_log(response); // Refined search $(response.responseText).find(`td`).children(":first-child").each(function (index) { let title = /([^\/]+)\/?$/g.exec($(this).text())[1]; // Check in case link is already the full url let fullUrl = $(this).attr('href').startsWith("//archive.org/download/") ? $(this).attr('href') : `${mainDir}/${$(this).attr('href')}` // GM_log($(this).attr('href')) if (refinedCompare(title, game)) { resultsDlcs.push({ name: title, url: fullUrl }); } }); // Normal search if (resultsDlcs.length == 0) { $(response.responseText).find(`td`).children(":first-child").each(function (index) { let title = /([^\/]+)\/?$/g.exec($(this).text())[1]; let fullUrl = $(this).attr('href').startsWith("//archive.org/download/") ? $(this).attr('href') : `${mainDir}/${$(this).attr('href')}` // GM_log($(this).attr('href')) if (compare(title, game)) { resultsDlcs.push({ name: title, url: fullUrl }); } }); } return resolve(true); } }) }); } // ========================================= // Emuparadise Function // ========================================= function searchEmuparadise() { var mainDir = "https://www.emuparadise.me/"; var consoleUrl; if (console == RAConsole.SNES) { consoleUrl = "Super_Nintendo_Entertainment_System_(SNES)_ROMs/List-All-Titles/5"; } if (console == RAConsole.NES) { consoleUrl = "Nintendo_Entertainment_System_ROMs/List-All-Titles/13"; } if (console == RAConsole.GAMEBOY) { consoleUrl = "Nintendo_Game_Boy_ROMs/List-All-Titles/12"; } if (console == RAConsole.GBC) { consoleUrl = "Nintendo_Game_Boy_Color_ROMs/List-All-Titles/11"; } if (console == RAConsole.GBA) { consoleUrl = "Nintendo_Gameboy_Advance_ROMs/List-All-Titles/31"; } if (console == RAConsole.N64) { consoleUrl = "Nintendo_64_ROMs/List-All-Titles/9"; } if (console == RAConsole.GCN) { consoleUrl = "Nintendo_Gamecube_ISOs/List-All-Titles/42" } if (console == RAConsole.NDS) { consoleUrl = "Nintendo_DS_ROMs/List-All-Titles/32"; } if (console == RAConsole.GENESIS) { consoleUrl = "Sega_Genesis_-_Sega_Megadrive_ROMs/List-All-Titles/6"; } if (console == RAConsole.MASTERSYSTEM) { consoleUrl = "Sega_Master_System_ROMs/List-All-Titles/15"; } if (console == RAConsole.SEGA32X) { consoleUrl = "Sega_32X_ROMs/61"; } if (console == RAConsole.SATURN) { consoleUrl = "Sega_Saturn_ISOs/List-All-Titles/3" } if (console == RAConsole.SEGACD) { consoleUrl = "Sega_CD_ISOs/List-All-Titles/10"; } if (console == RAConsole.GG) { consoleUrl = "Sega_Game_Gear_ROMs/List-All-Titles/14"; } if (console == RAConsole.NEOPOCKET) { consoleUrl = "Neo_Geo_Pocket_-_Neo_Geo_Pocket_Color_(NGPx)_ROMs/38"; } if (console == RAConsole.A2600) { consoleUrl = "Atari_2600_ROMs/List-All-Titles/49" } if (console == RAConsole.A7800) { consoleUrl = "Atari_7800_ROMs/47" } if (console == RAConsole.PCENGINE) { consoleUrl = "PC_Engine_-_TurboGrafx16_ROMs/List-All-Titles/16"; } if (console == RAConsole.APPLEII) { consoleUrl = "Apple_][_ROMs/List-All-Titles/24"; } if (console == RAConsole.PS1) { consoleUrl = "Sony_Playstation_ISOs/List-All-Titles/2"; } if (console == RAConsole.PS2) { consoleUrl = "Sony_Playstation_2_ISOs/List-All-Titles/41"; } if (console == RAConsole.PSP) { consoleUrl = "PSP_ISOs/List-All-Titles/44"; } if (console == RAConsole.PANASONIC3DO) { consoleUrl = "Panasonic_3DO_(3DO_Interactive_Multiplayer)_ISOs/List-All-Titles/20"; } return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: mainDir + consoleUrl, onload: function (response) { // GM_log(response); // Refined search $(response.responseText).find(`.index.gamelist`).each(function (index) { let epGameId = /([^\/]+)\/?$/g.exec($(this).attr('href'))[0]; // GM_log($(this).text()) if (refinedCompare($(this).text(), game)) { results.push({ name: $(this).text(), url: `https://www.emuparadise.me/roms/get-download.php?gid=${epGameId}&test=true` }); } }); // Normal search if (results.length == 0) { $(response.responseText).find(`.index.gamelist`).each(function (index) { let epGameId = /([^\/]+)\/?$/g.exec($(this).attr('href'))[0]; // GM_log($(this).text()) if (compare($(this).text(), game)) { results.push({ name: $(this).text(), url: `https://www.emuparadise.me/roms/get-download.php?gid=${epGameId}&test=true` }); } }); } return resolve(true); } }) }); } // ========================================= // Utility Functions // ========================================= function refinedCompare(a, b) { let str1 = simplify_title(a); let str2 = simplify_title(b); // GM_log("*Str 1: " + str1) // GM_log("*Str 2: " + str2) // GM_log(str1 == str2) if (str1 == str2) return true; else return false; } function compare(a, b) { // GM_log(a) let str1 = simplify_title(a); let str2 = simplify_title(b); // GM_log("Str 1: " + str1) // GM_log("Str 2: " + str2) // GM_log(str1.includes(str2)) if (str1.includes(str2)) return true; else return false; } // Remove unnecessary characters and region headering function simplify_title(str) { // Dreamcast TOSEC set puts versioning into the filename if (console == RAConsole.DREAMCAST) str = str.replace(/v[0-9].[0-9]{3}/gs, "") return str .replace(/\.(zip|7z)$/,"") .replace(/^The /g, '') .replace(", The", '') .replace(/'s/gs, '') .replace('&', 'and') .replace(/:|-| |\.|!|\?|\/|'/gs, '') .replace(/(\r\n|\n|\r)/gs, "") // remove line breaks .split('|')[0] .replace(',', "") // remove headers (old) // .replace(/\([\w\s]+\)/gs, "") // .replace(/\[[\w\s]+\]/gs, "") // remove headers (everything inside parenthesis) .replace(/\(.+\)/gs, "") .replace(/\[.+\]/gs, "") .toLowerCase() } function removeExt(str) { return str.replace(/\.(zip|7z|chd)$/,""); } function parseIso8601(time) { var parsed = ""; let regex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/; let groups = regex.exec(time); if (groups[6] != undefined) parsed = parsed.concat(`${groups[6]}h `) if (groups[7] != undefined) parsed = parsed.concat(`${groups[7]}m `) if (groups[8] != undefined) parsed = parsed.concat(`${groups[8]}s `) return parsed; } function isVideoUrl(url) { return (url.includes("www.youtu") || url.includes("www.twitch.tv")) } function toEmbedUrl(url) { if (url.includes("twitch") || url.includes("youtu")) { // GM_log(url); // https://regex101.com/r/jmV6qH/1 var regexYoutube = /(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)?([^\s&]+)/; // https://regex101.com/r/0RofWZ/1 var regexTwitch = /(?:https?:\/{2})?www\.twitch\.tv\/(:?[\S]+\/)?([\]?)?\/([\d]+)/; if (url.match(regexYoutube) != undefined) { return "https://www.youtube.com/embed/" + url.match(regexYoutube)[1]; } else if (url.match(regexTwitch) != undefined) { return "https://player.twitch.tv/?video=" + url.match(regexTwitch)[2] + "&parent=retroachievements.org&autoplay=false"; } } return ""; } } })();