NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Retro Enhanced: Pinchanzo Edition // @namespace pinchanzo // @version 0.1.1 // @description Additional functionality for retro gaming related sites. // @author Miagui // @match *://retroachievements.org/* // @license MIT // @updateURL https://openuserjs.org/meta/pinchanzo/Retro_Enhanced_Pinchanzo_Edition.meta.js // @downloadURL https://openuserjs.org/install/pinchanzo/RAE // @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.me // @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 = $(".login 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 playLaterList = await GM_getValue("playLaterGames", []); playLaterList = playLaterList.length != 0 ? JSON.parse(playLaterList) : []; var RAConsole = { GAMEBOY: "Game Boy", GBC: "Game Boy Color", GBA: "Game Boy Advance", NES: "NES", SNES: "SNES", N64: "Nintendo 64", NDS: "Nintendo DS", POKEMINI: "Pokemon Mini", VIRTUALBOY: "Virtual Boy", A2600: "Atari 2600", A7800: "Atari 7800", PCENGINE: "PC Engine", MASTERSYSTEM: "Master System", GG: "Game Gear", GENESIS: "Mega Drive", SEGA32X: "32X", SEGACD: "Sega CD", SATURN: "Saturn", DREAMCAST: "Dreamcast", PS1: "PlayStation", PSP: "PlayStation Portable", PANASONIC3DO: "3DO Interactive Multiplayer", NEOPOCKET: "Neo Geo Pocket", SG1000: "SG-1000", COLECO: "ColecoVision", MSX: "MSX", WONDERSWAN: "WonderSwan", VECTREX: "Vectrex", NEC8800: "PC-8000/8800", APPLEII: "Apple II", ARCADE: "Arcade" }; 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(playLaterList) // GM_log(page) // GM_log(sectionName) // Add Play Later List to nav $("#menuholder > li").eq(4).find(".divider") .eq(0).before(` <li> <a href="/setRequestList.php?u=${loggedUser}#playlater"> Play Later </a> </li>`) if (page == "/controlpanel.php") { $("#leftcontainer > .component").eq(3).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 (For Chrome need <a href="https://experienceleague.adobe.com/docs/target/using/experiences/vec/troubleshoot-composer/mixed-content.html?lang=en#task_FF297A08F66E47A588C14FD67C037B3A"> 'Insecure Content' set to 'Allow' </a> on site settings): </td> <td><input id="enhanced-epromsearch" type="checkbox" ${enableEmuparadise ? "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:</td> <td><input id="enhanced-gameplayvideo" type="checkbox" ${enableCustomBG ? "checked='checked'" : ""}></td> </tr> <tr> <td>Enable custom game page background:</td> <td><input id="enhanced-custombg" type="checkbox" ${enableCustomBG ? "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-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")) }) } // setRequestList.php?u=UserName && verify if user is the one logged else if (page == "/setRequestList.php" && //https://regex101.com/r/X0UMEo/1 /u=([^&]*)/g.exec(pageWithParams)[1] == loggedUser) { if (sectionName == "playlater") { $('html, body').animate({ scrollTop: 850 }, 100); } playLaterList.sort((a, b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0)); function list() { var list = ""; for (const prop in RAConsole) { if (RAConsole.hasOwnProperty(prop)) { const element = RAConsole[prop]; if (playLaterList.filter(playGame => playGame.console == element).length > 0) list = list.concat(`<h4 style="margin-top:1em;">${element}</h4> <table> <tbody> <tr> <th>Game</th> <th>Points</th> </tr> ${playLaterList.map(playGame => { if (playGame.console == element) return ` <tr> <td> <div class="bb_inline" style='display:flex;width:500px;'> <a href="/Game/${playGame.id}"> <img loading="lazy" alt="" title="${playGame.name}" src="${playGame.icon}" width="32" height="32" class="badgeimg"> ${playGame.tag? `~${playGame.tag}~` : ""} ${playGame.name} </a> </div> </td> <td>${playGame.points || 0} points</td> </tr>`}).join(' ')} </tbody> </table>`) } } return list; } $("table").after(`</br></br> <h3 id="playlater">Play Later</h3> ${(list())}`) } // 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 table img").attr("src") var tag = ""; var gameImg = $("#achievement .gamescreenshots img").eq(1).attr("src"); var isOnPlayLater = playLaterList.filter(playGame => playGame.id == gameId).length > 0; var rgxTag = /\~(.*?)\~/g; // 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 = []; // 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 if (gameImg != "/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" }) } // Update local data in case it changes. var index = playLaterList.findIndex(playGame => playGame.id == gameId); playLaterList[index] = { name: game, id: gameId, console: console, icon: icon, points: points, tag: tag }; // Clean if there is any undefined id. playLaterList = playLaterList.filter(playGame => playGame.id != undefined) // GM_log(gameId) await GM_setValue("playLaterGames", JSON.stringify(playLaterList)) // Play Later button $("#leftcontainer #achievement h4").eq(1).before(`<a style='display:block;float:right; cursor:pointer;margin-top:5px;' id='enhanced-playlater'> <h4 style="font-size: 1.25em;"> ${isOnPlayLater ? '✘ Remove' : '+ Add to' } Play Later </h4> </a>`); $("#leftcontainer #achievement h4").eq(1).parent().parent().css("margin-top", "2em") // Play Later Click $(document).on('click', '#enhanced-playlater', async function () { // getValue again incase has been updated on another window playLaterList = await GM_getValue("playLaterGames", []); playLaterList = playLaterList.length != 0 ? JSON.parse(playLaterList) : []; if (!isOnPlayLater) { playLaterList.push({ name: game, id: gameId, console: console, icon: icon, points: points, tag: tag }) } else { playLaterList = playLaterList.filter(playGame => playGame.id != gameId) } await GM_setValue("playLaterGames", JSON.stringify(playLaterList)); isOnPlayLater = playLaterList.filter(playGame => playGame.id == gameId).length > 0; $('#enhanced-playlater > h4').text(`${isOnPlayLater ? '✘ Remove' : '+ Add to' } Play Later`) }) // Prepare divs $("#rightcontainer > .gamescreenshots").append("<div id='roms'></div>") $("#rightcontainer > .gamescreenshots").append("<div id='speedruncom'></div>") var divRoms = $("#roms"); var divSpeedruncom = $("#speedruncom"); divRoms.css("margin-top", "1em") divSpeedruncom.css("margin-top", "1em"); 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 (tag != "" && console != RAConsole.ARCADE) { divRoms.append("<b>Searching roms for games with tags not supported.</b>"); } else if (isAvailable && tag == "") { var promise; // Deprecated feature to find roms for hacks and homebrews. // if (tag != "" && console != RAConsole.ARCADE) { // promise = searchCustom(); // collection.name = "Custom"; // } if (enableEmuparadise) { promise = searchEmuparadise(); collection.name = "Emuparadise"; collection.url = "https://www.emuparadise.me/roms-isos-games.php" } else { // resolve the promise so it the function don't stop working promise = Promise.resolve() } promise.then(() => { if (results.length == 0) { if (console == RAConsole.GAMEBOY) { collection.name = "[No-Intro] Game Boy" collection.url = "https://archive.org/download/nointro.gb" return searchArchive("https://archive.org/download/nointro.gb"); } else if (console == RAConsole.GBC) { collection.name = "[No-Intro] Game Boy Color" collection.url = "https://archive.org/download/nointro.gbc" return searchArchive("https://archive.org/download/nointro.gbc"); } else if (console == RAConsole.GBA) { collection.name = "[No-Intro] Game Boy Advance" collection.url = "https://archive.org/download/nointro.gba" return searchArchive("https://archive.org/download/nointro.gba"); } else if (console == RAConsole.NES) { collection.name = "[No-Intro] Nintendo Entertainment System" collection.url = "https://archive.org/download/nointro.nes" return searchArchive("https://archive.org/download/nointro.nes"); } else if (console == RAConsole.SNES) { collection.name = "[No-Intro] Super Nintendo Entertainment System" collection.url = "https://archive.org/download/nointro.snes" } else if (console == RAConsole.N64) { collection.name = "[No-Intro] Nintendo 64" collection.url = "https://archive.org/download/nointro.n64" return searchArchive("https://archive.org/download/nointro.n64") .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/nointro.n64dd"); else return; }) } else if (console == RAConsole.NDS) { collection.name = "[No-Intro] Nintendo DS Decrypted"; collection.url = "https://archive.org/download/noIntroNintendoDsDecrypted2020Jan20" return searchArchive("https://archive.org/download/noIntroNintendoDsDecrypted2020Jan20"); } else if (console == RAConsole.POKEMINI) { collection.name = "[No-Intro] Pokémon Mini" collection.url = "http://archive.org/download/nointro.poke-mini" return searchArchive("http://archive.org/download/nointro.poke-mini"); } else if (console == RAConsole.VIRTUALBOY) { collection.name = "[No-Intro] Virtual Boy" collection.url = "https://archive.org/download/nointro.vb" return searchArchive("https://archive.org/download/nointro.vb"); } 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.A7800) { collection.name = "[No-Intro] Atari 7800" collection.url = "https://archive.org/download/nointro.atari-7800" return searchArchive("https://archive.org/download/nointro.atari-7800"); } else if (console == RAConsole.ATARI7800) { collection.name = "[No-Intro] Atari 7800" collection.url = "https://archive.org/download/nointro.atari-7800" return searchArchive("https://archive.org/download/nointro.atari-7800"); } else if (console == RAConsole.PS1) { collection.name = "[REDUMP] PSX"; collection.url = "https://archive.org/download/Sony-Playstation-USA-Redump.org-2019-05-27" return searchArchive("https://archive.org/download/Sony-Playstation-USA-Redump.org-2019-05-27") .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/Sony-Playstation-EUR-Redump.org"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/SonyPlaystation-EUR-Part2-Redump.org2019-06-05"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/Sony-Playstation-JP-ASIA-Part-1"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/Sony-Playstation-JP-ASIA-Part-2"); else return; }) } else if (console == RAConsole.PSP) { collection.name = "[REDUMP & Ghostware] 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) return searchArchive("https://archive.org/download/PSNCollectionByGhostware"); else return; }) .then(() => { if (results.length == 0) return searchArchive("https://archive.org/download/PSP-DLC/PSP%20DLC/"); else return; }) } else if (console == RAConsole.SATURN) { collection.name = "[REDUMP] Sega Saturn"; collection.url = "https://archive.org/download/redump.ss.revival" return searchArchive("https://archive.org/download/redump.ss.revival"); } else if (console == RAConsole.DREAMCAST) { collection.name = "[REDUMP] 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 CD"; collection.url = "https://archive.org/download/redump.mcd.revival" return searchArchive("https://archive.org/download/redump.mcd.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.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 = "[TOSEC] Apple II" 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 2021" collection.url = "https://archive.org/download/no-intro_romsets/no-intro%20romsets/" return searchNoIntro2016(); } // exit if already has results } else return; }) .then(() => createDownloads()) } else { divRoms.append("<b>Searching roms for this system not supported.</b>"); } } 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 + ">" + 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 createSpeedrun() { divSpeedruncom.append("<h3>World Records</h3>"); divSpeedruncom.append(`<a href=${srGamelink}> <img style="display: block; width: 100%; object-fit: cover;" src=${srLogo}></img> </a>`) if (srRuns.length > 0) { srRuns.forEach((run) => { divSpeedruncom.append(`<div><a href=${run.link}>${run.category}:</a> ${run.time} by ${run.runner}</div>`) }); } else { divSpeedruncom.append("<div>Couldn't find this game on Speedrun.com</div>") } } function createVideo() { if (srVideoUrl != "") $("#achievement").children().eq(4) .after($(`<iframe style="display: block; width: 100%; height:315px;" 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 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((runsRes) => { let run = runsRes.data[0]; if (run != undefined && run.status.status != "rejected") { // GM_log(run.videos) if (srVideoUrl == "" && run.videos) srVideoUrl = toEmbedUrl(run.videos.links[0].uri); totalRuns++; return run; } }) .then((run) => { if (run == undefined) return false; // TODO: Resolve useless request if user is a guest. let isGuest = run.players[0].rel == "guest" ? true : false; return $.get(srRoot + "users/" + run.players[0].id) .then(userRes => { srRuns.push({ category: category.name, time: parseIso8601(run.times.primary), runner: isGuest ? run.players[0].name : userRes.data.names.international, link: run.videos ? run.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); // } // }) // }) // } 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.includes("[Hack]")) return; if (tag == "Hack" && !name.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.includes("[Hack]")) return; if (tag == "Hack" && !name.includes("[Hack]")) return; if (compare(name, game)) { results.push({ name: name, url: mainDir + $(this).attr('name') + ".zip" }); } }) } resolve(true); } }) }); } // https://archive.org/download/No-Intro-Collection_2016-01-03_Fixed function searchNoIntro2016() { var mainDir = "https://archive.org/download/No-Intro-Collection_2016-01-03_Fixed/"; var consoleDir = ""; var secondaryConsoleDir = ""; 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"; } 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); } }) }) } }) } // Generic Archives site function searchArchive(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)) { 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); } }) }); } // Generic Archives site 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.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.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); } }) }); } function refinedCompare(a, b) { let str1 = abstractify(a); let str2 = abstractify(b); // GM_log("Str 1: " + str1) // GM_log("Str 2: " + str2) if (str1 == str2) return true; else return false; } function compare(a, b) { // GM_log(a) let str1 = abstractify(a); let str2 = abstractify(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 abstractify(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(/^The /g, '') .replace(", The", '') .replace(/'s/gs, '') .replace('&', 'and') .replace(/:|-| |\.|\+|!|\/|'/gs, '') .split('|')[0] .replace(',', "") // remove headers .replace(/\([\w\s]+\)/gs, "") .replace(/\[[\w\s]+\]/gs, "") .toLowerCase() } 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 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 ""; } } })();