diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 6f3e866d..00000000 --- a/Gruntfile.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = function(grunt) { - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - - concat: { - dist: { - src: ['src/**/*.js'], - dest: 'dist/gameboy.js' - } - }, - - uglify: { - dist: { - files: { - "dist/gameboy.min.js": ["dist/gameboy.js"] - } - } - } - - }); - - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - - grunt.registerTask('default', ['concat', 'uglify']); -}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..aae767c4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 TheEmeraldStarr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app.js b/app.js index 610617d4..ac5d4135 100644 --- a/app.js +++ b/app.js @@ -1,603 +1,28 @@ -const { - checkServerIdentity -} = require('tls'); - -const express = require('express'), +const + express = require('express'), app = express(), http = require('http'), - https = require('https'), fs = require('fs'), - querystring = require('querystring'), - session = require('express-session'), - sanitizer = require('sanitizer'), - websocket = require('./ws-proxy.js'), - fetch = require('node-fetch'), - path = require("path"); + path = require('path'), + char_insert = require('./charinsert.js'); const config = JSON.parse(fs.readFileSync('./config.json', { encoding: 'utf8' })); -if (!config.prefix.startsWith('/')) { - config.prefix = `/${config.prefix}`; -} - -if (!config.prefix.endsWith('/')) { - config.prefix = `${config.prefix}/`; -} let server; -let server_protocol; -const server_options = { - key: fs.readFileSync('./ssl/default.key'), - cert: fs.readFileSync('./ssl/default.crt') -} -if (config.ssl == true) { - server = https.createServer(server_options, app); - server_protocol = 'https://'; -} else { - server = http.createServer(app); - server_protocol = 'http://'; -}; -// WebSocket Proxying -websocket(server); - -console.log(`Alloy Proxy now running on ${server_protocol}0.0.0.0:${config.port}! Proxy prefix is "${config.prefix}"!`); +server = http.createServer(app); server.listen(process.env.PORT || config.port); -btoa = (str) => { - str = new Buffer.from(str).toString('base64'); - return str; -}; - -atob = (str) => { - str = new Buffer.from(str, 'base64').toString('utf-8'); - return str; -}; - -rewrite_url = (dataURL, option) => { - var websiteURL; - var websitePath; - if (option == 'decode') { - websiteURL = atob(dataURL.split('/').splice(0, 1).join('/')); - websitePath = '/' + dataURL.split('/').splice(1).join('/'); - } else { - websiteURL = btoa(dataURL.split('/').splice(0, 3).join('/')); - websitePath = '/' + dataURL.split('/').splice(3).join('/'); - } - if (websitePath == '/') { - return `${websiteURL}`; - } else return `${websiteURL}${websitePath}`; -}; - -app.use(session({ - secret: 'alloy', - saveUninitialized: true, - resave: true, - /*cookieName: '__alloy_cookie_auth=yes', - duration: 30 * 60 * 1000, - activeDuration: 5 * 60 * 1000 */ -})); - -// We made our own version of body-parser instead, due to issues. -app.use((req, res, next) => { - if (req.method == 'POST') { - req.raw_body = ''; - req.on('data', chunk => { - req.raw_body += chunk.toString(); // convert buffer to string - }); - req.on('end', () => { - req.str_body = req.raw_body; - try { - req.body = JSON.parse(req.raw_body); - } catch (err) { - req.body = {} - } - next(); - }); - } else return next(); -}); - -app.use(`${config.prefix}utils/`, async (req, res, next) => { - if (req.url.startsWith('/assets/')) { - res.sendFile(__dirname + '/utils' + req.url); - } - if (req.query.url) { - let url = atob(req.query.url); - if (url.startsWith('https://') || url.startsWith('http://')) { - url = url; - } else if (url.startsWith('//')) { - url = 'http:' + url; - } else { - url = 'http://' + url; - } - return res.redirect(307, config.prefix + rewrite_url(url)); - } -}); - -/* -//Cookie Auth - - app.use(checkAuth); - - app.use(auth); - -function auth(req, res, next) { - -let user = new User({ - cookieName: '__alloy_cookie_auth=yes' - }); - - if (!req.signedCookies.user) { - var authHeader = req.headers.authorization; - if (!authHeader) { - var err = new Error('You are not authenticated!'); - err.status = 401; - next(err); - return; - } - var auth = new Buffer(authHeader.split(' ')[1], 'base64').toString().split(':'); - var pass = auth[1]; - if (user == '__alloy_cookie_auth=yes') { - res.cookie('user', 'admin', { - signed: true - }); - next(); // authorized - } else { - var err = new Error('You are not authenticated!'); - err.status = 401; - next(err); - } - } else { - if (req.signedCookies.user === 'admin') { - next(); - } else { - var err = new Error('You are not authenticated!'); - err.status = 401; - next(err); - } - } - }; - -// Check the auth of the routes => middleware functions -function checkAuth(req, res, next) { - console.log('checkAuth ' + req.url); -// don 't serve /secure to those not logged in => /secure if for those who are logged in -// you should add to this list, for each and every secure url - if (req.url.indexOf(`${config.prefix}session/`) === 0 && (!req.session || !req.session.authenticated)) { - res.render(fs.readFileSync('./utils/error/error.html', 'utf8').toString().replace('%ERROR%', `Error 401: The website '${sanitizer.sanitize(proxy.url.hostname)}' is not permitted!`), { - status: 403 - }); - return; - } - xt(); - } */ - -app.post(`${config.prefix}session/`, async (req, res, next) => { - let url = querystring.parse(req.raw_body).url; - if (url.startsWith('//')) { - url = 'http:' + url; - } else if (url.startsWith('https://') || url.startsWith('http://')) { - url = url - } else { - url = 'http://' + url - }; - - /* let cookies = {}; - if (request.headers.cookie !== undefined) { - cookies = cookie.parse(request.headers.cookie); - } - - console.log(cookies); - response.writeHead(200, { - 'SET-Cookie': ['__alloy_cookie_auth=yes', - `Permanent=Cookies; Max-Age=${60*60*24*30}`, - 'Secure=Secure; Secure', - 'HttpOnly=HttpOnly; HttpOnly', - 'Path=Path; Path=/cookie' - ] - }) - response.end('Coookie!!'); */ - - req.session.authenticated = true; -}); - -app.use(config.prefix, async (req, res, next) => { - var proxy = {}; - proxy.url = rewrite_url(req.url.slice(1), 'decode'); - proxy.url = { - href: proxy.url, - hostname: proxy.url.split('/').splice(2).splice(0, 1).join('/'), - origin: proxy.url.split('/').splice(0, 3).join('/'), - encoded_origin: btoa(proxy.url.split('/').splice(0, 3).join('/')), - path: '/' + proxy.url.split('/').splice(3).join('/'), - protocol: proxy.url.split('\:').splice(0, 1).join(''), - } - - proxy.url.encoded_origin = btoa(proxy.url.origin); - - proxy.requestHeaders = req.headers; - proxy.requestHeaders['host'] = proxy.url.hostname; - if (proxy.requestHeaders['referer']) { - let referer = '/' + String(proxy.requestHeaders['referer']).split('/').splice(3).join('/'); - - referer = rewrite_url(referer.replace(config.prefix, ''), 'decode'); - - if (referer.startsWith('https://') || referer.startsWith('http://')) { - referer = referer; - - } else referer = proxy.url.href; - - proxy.requestHeaders['referer'] = referer; - } - - - if (proxy.requestHeaders['origin']) { - let origin = '/' + String(proxy.requestHeaders['origin']).split('/').splice(3).join('/'); - - origin = rewrite_url(origin.replace(config.prefix, ''), 'decode'); - - if (origin.startsWith('https://') || origin.startsWith('http://')) { - - origin = origin.split('/').splice(0, 3).join('/'); - - } else origin = proxy.url.origin; - - proxy.requestHeaders['origin'] = origin; - } - if (proxy.requestHeaders.cookie) { - delete proxy.requestHeaders.cookie; - } - const httpAgent = new http.Agent({ - keepAlive: true - }); - const httpsAgent = new https.Agent({ - rejectUnauthorized: false, - keepAlive: true - }); - proxy.options = { - method: req.method, - headers: proxy.requestHeaders, - redirect: 'manual', - agent: function(_parsedURL) { - if (_parsedURL.protocol == 'http:') { - return httpAgent; - } else { - return httpsAgent; - } - } - }; - - if (req.method == 'POST') { - proxy.options.body = req.str_body; - } - if (proxy.url.hostname == 'discord.com' && proxy.url.path == '/') { - return res.redirect(307, config.prefix + rewrite_url('https://discord.com/login')); - }; - - if (proxy.url.hostname == 'www.reddit.com') { - return res.redirect(307, config.prefix + rewrite_url('https://old.reddit.com')); - }; - - if (!req.url.slice(1).startsWith(`${proxy.url.encoded_origin}/`)) { - return res.redirect(307, config.prefix + proxy.url.encoded_origin + '/'); - }; - - const blocklist = JSON.parse(fs.readFileSync('./blocklist.json', { - encoding: 'utf8' - })); - - let is_blocked = false; - - Array.from(blocklist).forEach(blocked_hostname => { - if (proxy.url.hostname == blocked_hostname) { - is_blocked = true; - } - }); - - if (is_blocked == true) { - return res.send(fs.readFileSync('./utils/error/error.html', 'utf8').toString().replace('%ERROR%', `Error 401: The website '${sanitizer.sanitize(proxy.url.hostname)}' is not permitted!`)) - } - - proxy.response = await fetch(proxy.url.href, proxy.options).catch(err => res.send(fs.readFileSync('./utils/error/error.html', 'utf8').toString().replace('%ERROR%', `Error 400: Could not make request to '${sanitizer.sanitize(proxy.url.href)}'!`))); - - if (typeof proxy.response.buffer != 'function') return; - - proxy.buffer = await proxy.response.buffer(); - - proxy.content_type = 'text/plain'; - - proxy.response.headers.forEach((e, i, a) => { - if (i == 'content-type') proxy.content_type = e; - }); - if (proxy.content_type == null || typeof proxy.content_type == 'undefined') proxy.content_type = 'text/html'; - - proxy.sendResponse = proxy.buffer; - - // Parsing the headers from the response to remove square brackets so we can set them as the response headers. - proxy.headers = Object.fromEntries( - Object.entries(JSON.parse(JSON.stringify(proxy.response.headers.raw()))) - .map(([key, val]) => [key, val[0]]) - ); - - // Parsing all the headers to remove all of the bad headers that could affect proxies performance. - Object.entries(proxy.headers).forEach(([header_name, header_value]) => { - if (header_name.startsWith('content-encoding') || header_name.startsWith('x-') || header_name.startsWith('cf-') || header_name.startsWith('strict-transport-security') || header_name.startsWith('content-security-policy')) { - delete proxy.headers[header_name]; - } - }); - - // If theres a location for a redirect in the response, then the proxy will get the response location then redirect you to the proxied version of the url. - if (proxy.response.headers.get('location')) { - return res.redirect(307, config.prefix + rewrite_url(String(proxy.response.headers.get('location')))); - } - - res.status(proxy.response.status); - res.set(proxy.headers); - res.contentType(proxy.content_type); - if (proxy.content_type.startsWith('text/html')) { - req.session.url = proxy.url.origin; - proxy.sendResponse = proxy.sendResponse.toString() - .replace(/integrity="(.*?)"/gi, '') - .replace(/nonce="(.*?)"/gi, '') - .replace(/(href|src|poster|data|action|srcset)="\/\/(.*?)"/gi, `$1` + `="http://` + `$2` + `"`) - .replace(/(href|src|poster|data|action|srcset)='\/\/(.*?)'/gi, `$1` + `='http://` + `$2` + `'`) - .replace(/(href|src|poster|data|action|srcset)="\/(.*?)"/gi, `$1` + `="${config.prefix}${proxy.url.encoded_origin}/` + `$2` + `"`) - .replace(/(href|src|poster|data|action|srcset)='\/(.*?)'/gi, `$1` + `='${config.prefix}${proxy.url.encoded_origin}/` + `$2` + `'`) - .replace(/'(https:\/\/|http:\/\/)(.*?)'/gi, function(str) { - str = str.split(`'`).slice(1).slice(0, -1).join(``); - return `'${config.prefix}${rewrite_url(str)}'` - }) - .replace(/"(https:\/\/|http:\/\/)(.*?)"/gi, function(str) { - str = str.split(`"`).slice(1).slice(0, -1).join(``); - return `"${config.prefix}${rewrite_url(str)}"` - }) - .replace(/(window|document).location.href/gi, `"${proxy.url.href}"`) - .replace(/(window|document).location.hostname/gi, `"${proxy.url.hostname}"`) - .replace(/(window|document).location.pathname/gi, `"${proxy.url.path}"`) - .replace(/location.href/gi, `"${proxy.url.href}"`) - .replace(/location.hostname/gi, `"${proxy.url.hostname}"`) - .replace(/location.pathname/gi, `"${proxy.url.path}"`) - .replace(//gi, ``); - - // Temp hotfix for Youtube search bar until my script injection can fix it. - - if (proxy.url.hostname == 'www.youtube.com') { - proxy.sendResponse = proxy.sendResponse.replace(/\/results/gi, `${config.prefix}${proxy.url.encoded_origin}/results`); - }; - } else if (proxy.content_type.startsWith('text/css')) { - proxy.sendResponse = proxy.sendResponse.toString() - .replace(/url\("\/\/(.*?)"\)/gi, `url("http://` + `$1` + `")`) - .replace(/url\('\/\/(.*?)'\)/gi, `url('http://` + `$1` + `')`) - .replace(/url\(\/\/(.*?)\)/gi, `url(http://` + `$1` + `)`) - .replace(/url\("\/(.*?)"\)/gi, `url("${config.prefix}${proxy.url.encoded_origin}/` + `$1` + `")`) - .replace(/url\('\/(.*?)'\)/gi, `url('${config.prefix}${proxy.url.encoded_origin}/` + `$1` + `')`) - .replace(/url\(\/(.*?)\)/gi, `url(${config.prefix}${proxy.url.encoded_origin}/` + `$1` + `)`) - .replace(/"(https:\/\/|http:\/\/)(.*?)"/gi, function(str) { - str = str.split(`"`).slice(1).slice(0, -1).join(``); - return `"${config.prefix}${rewrite_url(str)}"` - }) - .replace(/'(https:\/\/|http:\/\/)(.*?)'/gi, function(str) { - str = str.split(`'`).slice(1).slice(0, -1).join(``); - return `'${config.prefix}${rewrite_url(str)}'` - }) - .replace(/\((https:\/\/|http:\/\/)(.*?)\)/gi, function(str) { - str = str.split(`(`).slice(1).join(``).split(')').slice(0, -1).join(''); - return `(${config.prefix}${rewrite_url(str)})` - }); - - }; - // We send the response from the server rewritten. - res.send(proxy.sendResponse); -}); - -app.post('/', async (req, res) => { - +app.post('/', async(req, res) => { switch (req.url) { case '/': return res.send(fs.readFileSync(path.join(__dirname, 'public', 'index.html'), 'utf8')); } }); -/* app.get('/'), async (req, res) => { - charInsert = str => { - var output = ''; - - str.split(' ').forEach((word, word_index) => (word.split('').forEach((chr, chr_index) => output += (!chr_index || chr_index == word.length) ? '&#' + chr.charCodeAt() + '' : '​...&#' + chr.charCodeAt() + '​'), output += word_index != str.split(' ').length - 1 ? ' ' : '')); - - return output - }, - result = fs.readFileSync(path.join(__dirname, 'public', 'index.html'), 'utf8').replace(/charinsert{([\s\S]*?)}/g, (match, str) => charInsert(str)); - - fs.writeFileSync(path.join(__dirname, 'public', 'page.html'), result); -} - - -app.get('/'), async (req, res) => { - charInsert = str => { - var output = ''; - - str.split(' ').forEach((word, word_index) => (word.split('').forEach((chr, chr_index) => output += (!chr_index || chr_index == word.length) ? '&#' + chr.charCodeAt() + '' : '​...&#' + chr.charCodeAt() + '​'), output += word_index != str.split(' ').length - 1 ? ' ' : '')); - - return output - }, - result = fs.readFileSync(path.join(__dirname, 'public', 'page.html'), 'utf8').replace(/charinsert{([\s\S]*?)}/g, (match, str) => charInsert(str)); - - fs.writeFileSync(path.join(__dirname, 'public', 'index.html'), result); -} */ - //Querystrings -app.get('/', async (req, res, t) => res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages,index.html,info.html,archive,archive,hidden.html'.split(',')['/,/?in,/?fg,/?rr,/?j'.split(',').indexOf(req.url) + 1], ',surf.html,f.html,run.html,frames,redirects3,proxnav5,nav7'.replace(/,[^,]+/g, e => ([] + e.match(/\D+/)).repeat(+e.match(/\d+/) + 1)).split(',')[t = 'z,fg,rr,k,dd,n,yh,ym,a,b,y,e,d,p,c,f,g,h,i,m,t,x'.split(',').indexOf(req.url.slice(2)) + 1], (t = ',,,,krunker,discordprox,chatbox,ythub,ytmobile,alloy,node,youtube,pydodge,discordhub,pmprox,credits,flash,gtools,games5,icons,gba,terms,bookmarklets'.split(',')[t]) && t + '.html'), 'utf8'))); -//Querystrings Old -/* app.get('/', async (req, res) => { +app.get('/', async(req, res, t) => res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages,index.html,info.html,archive,archive,hidden.html'.split(',')['/,/?in,/?fg,/?rr,/?j'.split(',').indexOf(req.url) + 1], ',surf.html,f.html,run.html,frames,redirects3,proxnav5,nav7'.replace(/,[^,]+/g, e => ([] + e.match(/\D+/)).repeat(+e.match(/\d+/) + 1)).split(',')[t = 'z,fg,rr,k,dd,n,yh,ym,a,b,y,e,d,p,c,f,g,h,i,m,t,x'.split(',').indexOf(req.url.slice(2)) + 1], (t = ',,,,krunker,discordprox,chatbox,ythub,ytmobile,alloy,node,youtube,pydodge,discordhub,pmprox,credits,flash,gtools,games5,icons,gba,terms,bookmarklets'.split(',')[t]) && t + '.html'), 'utf8'))); - - - const path = require("path"); //Use this for path. - - fs.readFileSync( path, options ); - - Use this for improved navigation. Massive help from MikeLime and Duce. - - if (req.url == '/?querystringhere') { - return res.send(fs.readFileSync(path.resolve() + 'filepath', { - encoding: 'utf8' - })); - } - var hbsites = {}; - && hostname == hbsites - - - switch (req.url) { - case '/': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'index.html'), 'utf8')); - } - - switch (req.url) { - case '/?z': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'surf.html'), 'utf8')); - } - - switch (req.url) { - case '/?a': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'proxnav', 'alloy.html'), 'utf8')); - } - - switch (req.url) { - case '/?dd': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'redirects', 'discordprox.html'), 'utf8')); - } - - switch (req.url) { - case '/?b': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'proxnav', 'node.html'), 'utf8')); - } - - switch (req.url) { - case '/?y': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'proxnav', 'youtube.html'), 'utf8')); - } - - switch (req.url) { - case '/?e': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'proxnav', 'pydodge.html'), 'utf8')); - } - - switch (req.url) { - case '/?d': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'proxnav', 'discordhub.html'), 'utf8')); - } - - switch (req.url) { - case '/?c': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'credits.html'), 'utf8')); - } - - switch (req.url) { - case '/?f': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'flash.html'), 'utf8')); - } - - switch (req.url) { - case '/?g': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'gtools.html'), 'utf8')); - } - - switch (req.url) { - case '/?h': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'games5.html'), 'utf8')); - } - - switch (req.url) { - case '/?i': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'icons.html'), 'utf8')); - } - - switch (req.url) { - case '/?in': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'info.html'), 'utf8')); - } - - switch (req.url) { - case '/?k': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'frames', 'krunker.html'), 'utf8')); - } - - switch (req.url) { - case '/?m': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'gba.html'), 'utf8')); - } - - switch (req.url) { - case '/?n': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'redirects', 'chatbox.html'), 'utf8')); - } - - switch (req.url) { - case '/?p': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'proxnav', 'pmprox.html'), 'utf8')); - } - - switch (req.url) { - case '/?t': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'terms.html'), 'utf8')); - } - - switch (req.url) { - case '/?x': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'nav', 'bookmarklets.html'), 'utf8')); - } - - switch (req.url) { - case '/?yh': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'redirects', 'ythub.html'), 'utf8')); - } - - switch (req.url) { - case '/?ym': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'pages', 'redirects', 'ytmobile.html'), 'utf8')); - } - - switch (req.url) { - case '/?fg': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'archive', 'f.html'), 'utf8')); - } - - switch (req.url) { - case '/?rr': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'archive', 'run.html'), 'utf8')); - } - - // Frames Page - - switch (req.url) { - case '/?j': - return res.send(fs.readFileSync(path.join(__dirname, 'public', 'hidden.html'), 'utf8')) - } - -}); */ - -app.use('/', express.static('public')); - -app.use(async (req, res, next) => { - if (req.headers['referer']) { - - let referer = '/' + String(req.headers['referer']).split('/').splice(3).join('/'); - - referer = rewrite_url(referer.replace(config.prefix, ''), 'decode').split('/').splice(0, 3).join('/'); - - if (referer.startsWith('https://') || referer.startsWith('http://')) { - res.redirect(307, config.prefix + btoa(referer) + req.url) - } else { - if (req.session.url) { - - res.redirect(307, config.prefix + btoa(req.session.url) + req.url) - - } else return next(); - } - } else if (req.session.url) { - - res.redirect(307, config.prefix + btoa(req.session.url) + req.url) - - } else return next(); -}); \ No newline at end of file +app.use(char_insert.static(path.join(__dirname, 'public'))); \ No newline at end of file diff --git a/blocklist.json b/blocklist.json deleted file mode 100644 index 4676dedc..00000000 --- a/blocklist.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "add-your-website-url.blocked" -] diff --git a/charinsert.js b/charinsert.js new file mode 100644 index 00000000..eed16843 --- /dev/null +++ b/charinsert.js @@ -0,0 +1,48 @@ +var fs = require('fs'), + path = require('path'), + mime = require('mime-types'), + char_insert = (str, hash) => { + var output = ''; + + str.split(' ').forEach((word, word_index) => (word.split('').forEach((chr, chr_index) => output += (!chr_index || chr_index == word.length) ? '&#' + chr.charCodeAt() + '' : '​' + hash + '&#' + chr.charCodeAt() + '​'), output += word_index != str.split(' ').length - 1 ? ' ' : '')); + + return output + }, + hash = s => { for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h ^ h >>> 9 }, + express_ip = req => { + var ip = null, + methods = [req.headers['cf-connecting-ip'], req.headers['x-real-ip'], req.headers['x-forwarded-for']]; + + methods.filter(method => method).forEach(method => { + if (ip) return; + + ip = method; + + if (ip.includes(',')) { + ip = ip.split(',')[ip.split(',').length - 1].replace(' ', ''); + if (ip.length > 15) ip = ip.split(',')[0].replace(' ', ''); + } + }); + + return ip || '127.0.0.1'; + }; + +module.exports = { + static: public_path => (req, res, next) => { + var pub_file = path.join(public_path, req.url); + + if (fs.existsSync(pub_file)) { + if (fs.statSync(pub_file).isDirectory()) pub_file = path.join(pub_file, 'index.html'); + if (!fs.existsSync(pub_file)) return next(); + + var mime_type = mime.lookup(pub_file), + data = fs.readFileSync(pub_file), + ext = path.extname(pub_file); + + if (ext == '.html') data = data.toString('utf8').replace(/char_?insert{([\s\S]*?)}/gi, (match, str) => char_insert(str, hash(express_ip(req)))); + + res.contentType(mime_type).send(data); + } else next(); + }, + parse: string => char_insert(string, '-1231'), +} \ No newline at end of file diff --git a/config.json b/config.json index d4799b53..67524245 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,4 @@ { - "port": "8081", - "prefix": "/fetch/", + "port": "8080", "ssl": false } \ No newline at end of file diff --git a/dist/gameboy.js b/dist/gameboy.js deleted file mode 100644 index 7cde3e40..00000000 --- a/dist/gameboy.js +++ /dev/null @@ -1,3038 +0,0 @@ -function loadboot(p) { - var boot = [ - 0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E, - 0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0, - 0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B, - 0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9, - 0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20, - 0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04, - 0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2, - 0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06, - 0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xE2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20, - 0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17, - 0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, - 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, - 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, - 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C, - 0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x00, 0x00, 0x23, 0x7D, 0xFE, 0x34, 0x20, - 0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x00, 0x00, 0x3E, 0x01, 0xE0, 0x50 - ]; - - for (var i in boot) { - p.memory[i] = boot[i]; - } - p.r.pc = 0; - p.usingBootRom = true; -} - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// CPU class -var CPU = function(gameboy) { - this.gameboy = gameboy; - - this.r = {A:0, F: 0, B:0, C:0, D:0, E:0, H:0, L:0, pc:0, sp:0}; - this.IME = true; - this.clock = {c: 0, serial: 0}; - this.isHalted = false; - this.isPaused = false; - this.usingBootRom = false; - - this.createDevices(); -}; - -CPU.INTERRUPTS = { - VBLANK: 0, - LCDC: 1, - TIMER: 2, - SERIAL: 3, - HILO: 4 -}; -CPU.interruptRoutines = { - 0: function(p){GameboyJS.cpuOps.RSTn(p, 0x40);}, - 1: function(p){GameboyJS.cpuOps.RSTn(p, 0x48);}, - 2: function(p){GameboyJS.cpuOps.RSTn(p, 0x50);}, - 3: function(p){GameboyJS.cpuOps.RSTn(p, 0x58);}, - 4: function(p){GameboyJS.cpuOps.RSTn(p, 0x60);} -}; - -CPU.prototype.createDevices = function() { - this.memory = new GameboyJS.Memory(this); - this.timer = new GameboyJS.Timer(this, this.memory); - this.apu = new GameboyJS.APU(this.memory); - - this.SERIAL_INTERNAL_INSTR = 512; // instr to wait per bit if internal clock - this.enableSerial = 0; - this.serialHandler = GameboyJS.ConsoleSerial; -}; - -CPU.prototype.reset = function() { - this.memory.reset(); - - this.r.sp = 0xFFFE; -}; - -CPU.prototype.loadRom = function(data) { - this.memory.setRomData(data); -}; - -CPU.prototype.getRamSize = function() { - var size = 0; - switch (this.memory.rb(0x149)) { - case 1: - size = 2048; - break; - case 2: - size = 2048 * 4; - break; - case 3: - size = 2048 * 16; - break; - } - - return size; -}; - -CPU.prototype.getGameName = function() { - var name = ''; - for (var i = 0x134; i < 0x143; i++) { - var char = this.memory.rb(i) || 32; - name += String.fromCharCode(char); - } - - return name; -}; - -// Start the execution of the emulator -CPU.prototype.run = function() { - if (this.usingBootRom) { - this.r.pc = 0x0000; - } else { - this.r.pc = 0x0100; - } - this.frame(); -}; - -CPU.prototype.stop = function() { - clearTimeout(this.nextFrameTimer); -}; - -// Fetch-and-execute loop -// Will execute instructions for the duration of a frame -// -// The screen unit will notify the vblank period which -// is considered the end of a frame -// -// The function is called on a regular basis with a timeout -CPU.prototype.frame = function() { - if (!this.isPaused) { - this.nextFrameTimer = setTimeout(this.frame.bind(this), 1000 / GameboyJS.Screen.physics.FREQUENCY); - } - - try { - var vblank = false; - while (!vblank) { - var oldInstrCount = this.clock.c; - if (!this.isHalted) { - var opcode = this.fetchOpcode(); - GameboyJS.opcodeMap[opcode](this); - this.r.F &= 0xF0; // tmp fix - - if (this.enableSerial) { - var instr = this.clock.c - oldInstrCount; - this.clock.serial += instr; - if (this.clock.serial >= 8 * this.SERIAL_INTERNAL_INSTR) { - this.endSerialTransfer(); - } - } - } else { - this.clock.c += 4; - } - - var elapsed = this.clock.c - oldInstrCount; - vblank = this.gpu.update(elapsed); - this.timer.update(elapsed); - this.input.update(); - this.apu.update(elapsed); - this.checkInterrupt(); - } - this.clock.c = 0; - } catch (e) { - this.gameboy.handleException(e); - } -}; - -CPU.prototype.fetchOpcode = function() { - var opcode = this.memory.rb(this.r.pc++); - if (opcode === undefined) {console.log(opcode + ' at ' + (this.r.pc-1).toString(16));this.stop();return;} - if (!GameboyJS.opcodeMap[opcode]) { - console.error('Unknown opcode '+opcode.toString(16)+' at address '+(this.r.pc-1).toString(16)+', stopping execution...'); - this.stop(); - return null; - } - - return opcode; -}; - -// read register -CPU.prototype.rr = function(register) { - return this.r[register]; -}; - -// write register -CPU.prototype.wr = function(register, value) { - this.r[register] = value; -}; - -CPU.prototype.halt = function() { - this.isHalted = true; -}; -CPU.prototype.unhalt = function() { - this.isHalted = false; -}; -CPU.prototype.pause = function() { - this.isPaused = true; -}; -CPU.prototype.unpause = function() { - if (this.isPaused) { - this.isPaused = false; - this.frame(); - } -}; - -// Look for interrupt flags -CPU.prototype.checkInterrupt = function() { - if (!this.IME) { - return; - } - for (var i = 0; i < 5; i++) { - var IFval = this.memory.rb(0xFF0F); - if (GameboyJS.Util.readBit(IFval, i) && this.isInterruptEnable(i)) { - IFval &= (0xFF - (1<> (7-pixel)) + ((b2 & mask) >> (7-pixel))*2; - pixelData[line * 8 + pixel] = colorValue; - } - } - - var i = 0; - while (pixelData.length) { - console.log(i++ + ' ' + pixelData.splice(0, 8).join('')); - } -}; - -Debug.list_visible_sprites = function(gameboy) { - var memory = gameboy.cpu.memory; - var indexes = new Array(); - for (var i = 0xFE00; i < 0xFE9F; i += 4) { - var x = memory.oamram(i + 1); - var y = memory.oamram(i); - var tileIndex = memory.oamram(i + 2); - if (x == 0 || x >= 168) { - continue; - } - indexes.push({oamIndex:i, x:x, y:y, tileIndex:tileIndex}); - } - - return indexes; -}; -GameboyJS.Debug = Debug; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; -var Screen; -var GPU = function(screen, cpu) { - this.cpu = cpu; - this.screen = screen; - - this.LCDC= 0xFF40; - this.STAT= 0xFF41; - this.SCY = 0xFF42; - this.SCX = 0xFF43; - this.LY = 0xFF44; - this.LYC = 0xFF45; - this.BGP = 0xFF47; - this.OBP0= 0xFF48; - this.OBP1= 0xFF49; - this.WY = 0xFF4A; - this.WX = 0xFF4B; - - this.vram = cpu.memory.vram.bind(cpu.memory); - - this.OAM_START = 0xFE00; - this.OAM_END = 0xFE9F; - this.deviceram = cpu.memory.deviceram.bind(cpu.memory); - this.oamram = cpu.memory.oamram.bind(cpu.memory); - this.VBLANK_TIME = 70224; - this.clock = 0; - this.mode = 2; - this.line = 0; - - Screen = GameboyJS.Screen; - this.buffer = new Array(Screen.physics.WIDTH * Screen.physics.HEIGHT); - this.tileBuffer = new Array(8); - this.bgTileCache = {}; -}; - -GPU.tilemap = { - HEIGHT: 32, - WIDTH: 32, - START_0: 0x9800, - START_1: 0x9C00, - LENGTH: 0x0400 // 1024 bytes = 32*32 -}; - -GPU.prototype.update = function(clockElapsed) { - this.clock += clockElapsed; - var vblank = false; - - switch (this.mode) { - case 0: // HBLANK - if (this.clock >= 204) { - this.clock -= 204; - this.line++; - this.updateLY(); - if (this.line == 144) { - this.setMode(1); - vblank = true; - this.cpu.requestInterrupt(GameboyJS.CPU.INTERRUPTS.VBLANK); - this.drawFrame(); - } else { - this.setMode(2); - } - } - break; - case 1: // VBLANK - if (this.clock >= 456) { - this.clock -= 456; - this.line++; - if (this.line > 153) { - this.line = 0; - this.setMode(2); - } - this.updateLY(); - } - - break; - case 2: // SCANLINE OAM - if (this.clock >= 80) { - this.clock -= 80; - this.setMode(3); - } - break; - case 3: // SCANLINE VRAM - if (this.clock >= 172) { - this.clock -= 172; - this.drawScanLine(this.line); - this.setMode(0); - } - break; - } - - return vblank; -}; - -GPU.prototype.updateLY = function() { - this.deviceram(this.LY, this.line); - var STAT = this.deviceram(this.STAT); - if (this.deviceram(this.LY) == this.deviceram(this.LYC)) { - this.deviceram(this.STAT, STAT | (1 << 2)); - if (STAT & (1 << 6)) { - this.cpu.requestInterrupt(GameboyJS.CPU.INTERRUPTS.LCDC); - } - } else { - this.deviceram(this.STAT, STAT & (0xFF - (1 << 2))); - } -}; - -GPU.prototype.setMode = function(mode) { - this.mode = mode; - var newSTAT = this.deviceram(this.STAT); - newSTAT &= 0xFC; - newSTAT |= mode; - this.deviceram(this.STAT, newSTAT); - - if (mode < 3) { - if (newSTAT & (1 << (3+mode))) { - this.cpu.requestInterrupt(GameboyJS.CPU.INTERRUPTS.LCDC); - } - } -}; - -// Push one scanline into the main buffer -GPU.prototype.drawScanLine = function(line) { - var LCDC = this.deviceram(this.LCDC); - var enable = GameboyJS.Util.readBit(LCDC, 7); - if (enable) { - var lineBuffer = new Array(Screen.physics.WIDTH); - this.drawBackground(LCDC, line, lineBuffer); - this.drawSprites(LCDC, line, lineBuffer); - // TODO draw a line for the window here too - } -}; - -GPU.prototype.drawFrame = function() { - var LCDC = this.deviceram(this.LCDC); - var enable = GameboyJS.Util.readBit(LCDC, 7); - if (enable) { - //this.drawSprites(LCDC); - this.drawWindow(LCDC); - } - this.bgTileCache = {}; - this.screen.render(this.buffer); -}; - -GPU.prototype.drawBackground = function(LCDC, line, lineBuffer) { - if (!GameboyJS.Util.readBit(LCDC, 0)) { - return; - } - - var mapStart = GameboyJS.Util.readBit(LCDC, 3) ? GPU.tilemap.START_1 : GPU.tilemap.START_0; - - var dataStart, signedIndex = false; - if (GameboyJS.Util.readBit(LCDC, 4)) { - dataStart = 0x8000; - } else { - dataStart = 0x8800; - signedIndex = true; - } - - var bgx = this.deviceram(this.SCX); - var bgy = this.deviceram(this.SCY); - var tileLine = ((line + bgy) & 7); - - // browse BG tilemap for the line to render - var tileRow = ((((bgy + line) / 8) | 0) & 0x1F); - var firstTile = ((bgx / 8) | 0) + 32 * tileRow; - var lastTile = firstTile + Screen.physics.WIDTH / 8 + 1; - if ((lastTile & 0x1F) < (firstTile & 0x1F)) { - lastTile -= 32; - } - var x = (firstTile & 0x1F) * 8 - bgx; // x position of the first tile's leftmost pixel - for (var i = firstTile; i != lastTile; i++, (i & 0x1F) == 0 ? i-=32 : null) { - var tileIndex = this.vram(i + mapStart); - - if (signedIndex) { - tileIndex = GameboyJS.Util.getSignedValue(tileIndex) + 128; - } - - // try to retrieve the tile data from the cache, or use readTileData() to read from ram - // TODO find a better cache system now that the BG is rendered line by line - var tileData = this.bgTileCache[tileIndex] || (this.bgTileCache[tileIndex] = this.readTileData(tileIndex, dataStart)); - - this.drawTileLine(tileData, tileLine); - this.copyBGTileLine(lineBuffer, this.tileBuffer, x); - x += 8; - } - - this.copyLineToBuffer(lineBuffer, line); -}; - -// Copy a tile line from a tileBuffer to a line buffer, at a given x position -GPU.prototype.copyBGTileLine = function(lineBuffer, tileBuffer, x) { - // copy tile line to buffer - for (var k = 0; k < 8; k++, x++) { - if (x < 0 || x >= Screen.physics.WIDTH) continue; - lineBuffer[x] = tileBuffer[k]; - } -}; - -// Copy a scanline into the main buffer -GPU.prototype.copyLineToBuffer = function(lineBuffer, line) { - var bgPalette = GPU.getPalette(this.deviceram(this.BGP)); - - for (var x = 0; x < Screen.physics.WIDTH; x++) { - var color = lineBuffer[x]; - this.drawPixel(x, line, bgPalette[color]); - } -}; - -// Write a line of a tile (8 pixels) into a buffer array -GPU.prototype.drawTileLine = function(tileData, line, xflip, yflip) { - xflip = xflip | 0; - yflip = yflip | 0; - var l = yflip ? 7 - line : line; - var byteIndex = l * 2; - var b1 = tileData[byteIndex++]; - var b2 = tileData[byteIndex++]; - - var offset = 8; - for (var pixel = 0; pixel < 8; pixel++) { - offset--; - var mask = (1 << offset); - var colorValue = ((b1 & mask) >> offset) + ((b2 & mask) >> offset)*2; - var p = xflip ? offset : pixel; - this.tileBuffer[p] = colorValue; - } -}; - -GPU.prototype.drawSprites = function(LCDC, line, lineBuffer) { - if (!GameboyJS.Util.readBit(LCDC, 1)) { - return; - } - var spriteHeight = GameboyJS.Util.readBit(LCDC, 2) ? 16 : 8; - - var sprites = new Array(); - for (var i = this.OAM_START; i < this.OAM_END && sprites.length < 10; i += 4) { - var y = this.oamram(i); - var x = this.oamram(i+1); - var index = this.oamram(i+2); - var flags = this.oamram(i+3); - - if (y - 16 > line || y - 16 < line - spriteHeight) { - continue; - } - sprites.push({x:x, y:y, index:index, flags:flags}) - } - - if (sprites.length == 0) return; - - // cache object to store read tiles from this frame - var cacheTile = {}; - var spriteLineBuffer = new Array(Screen.physics.WIDTH); - - for (var i = 0; i < sprites.length; i++) { - var sprite = sprites[i]; - var tileLine = line - sprite.y + 16; - var paletteNumber = GameboyJS.Util.readBit(flags, 4); - var xflip = GameboyJS.Util.readBit(sprite.flags, 5); - var yflip = GameboyJS.Util.readBit(sprite.flags, 6); - var tileData = cacheTile[sprite.index] || (cacheTile[sprite.index] = this.readTileData(sprite.index, 0x8000, spriteHeight * 2)); - this.drawTileLine(tileData, tileLine, xflip, yflip); - this.copySpriteTileLine(spriteLineBuffer, this.tileBuffer, sprite.x - 8, paletteNumber); - } - - this.copySpriteLineToBuffer(spriteLineBuffer, line); -}; - -// Copy a tile line from a tileBuffer to a line buffer, at a given x position -GPU.prototype.copySpriteTileLine = function(lineBuffer, tileBuffer, x, palette) { - // copy tile line to buffer - for (var k = 0; k < 8; k++, x++) { - if (x < 0 || x >= Screen.physics.WIDTH || tileBuffer[k] == 0) continue; - lineBuffer[x] = {color:tileBuffer[k], palette: palette}; - } -}; - -// Copy a sprite scanline into the main buffer -GPU.prototype.copySpriteLineToBuffer = function(spriteLineBuffer, line) { - var spritePalettes = {}; - spritePalettes[0] = GPU.getPalette(this.deviceram(this.OBP0)); - spritePalettes[1] = GPU.getPalette(this.deviceram(this.OBP1)); - - for (var x = 0; x < Screen.physics.WIDTH; x++) { - if (!spriteLineBuffer[x]) continue; - var color = spriteLineBuffer[x].color; - if (color === 0) continue; - var paletteNumber = spriteLineBuffer[x].palette; - this.drawPixel(x, line, spritePalettes[paletteNumber][color]); - } -}; - -GPU.prototype.drawTile = function(tileData, x, y, buffer, bufferWidth, xflip, yflip, spriteMode) { - xflip = xflip | 0; - yflip = yflip | 0; - spriteMode = spriteMode | 0; - var byteIndex = 0; - for (var line = 0; line < 8; line++) { - var l = yflip ? 7 - line : line; - var b1 = tileData[byteIndex++]; - var b2 = tileData[byteIndex++]; - - for (var pixel = 0; pixel < 8; pixel++) { - var mask = (1 << (7-pixel)); - var colorValue = ((b1 & mask) >> (7-pixel)) + ((b2 & mask) >> (7-pixel))*2; - if (spriteMode && colorValue == 0) continue; - var p = xflip ? 7 - pixel : pixel; - var bufferIndex = (x + p) + (y + l) * bufferWidth; - buffer[bufferIndex] = colorValue; - } - } -}; - -// get an array of tile bytes data (16 entries for 8*8px) -GPU.prototype.readTileData = function(tileIndex, dataStart, tileSize) { - tileSize = tileSize || 0x10; // 16 bytes / tile by default (8*8 px) - var tileData = new Array(); - - var tileAddressStart = dataStart + (tileIndex * 0x10); - for (var i = tileAddressStart; i < tileAddressStart + tileSize; i++) { - tileData.push(this.vram(i)); - } - - return tileData; -}; - -GPU.prototype.drawWindow = function(LCDC) { - if (!GameboyJS.Util.readBit(LCDC, 5)) { - return; - } - - var buffer = new Array(256*256); - var mapStart = GameboyJS.Util.readBit(LCDC, 6) ? GPU.tilemap.START_1 : GPU.tilemap.START_0; - - var dataStart, signedIndex = false; - if (GameboyJS.Util.readBit(LCDC, 4)) { - dataStart = 0x8000; - } else { - dataStart = 0x8800; - signedIndex = true; - } - - // browse Window tilemap - for (var i = 0; i < GPU.tilemap.LENGTH; i++) { - var tileIndex = this.vram(i + mapStart); - - if (signedIndex) { - tileIndex = GameboyJS.Util.getSignedValue(tileIndex) + 128; - } - - var tileData = this.readTileData(tileIndex, dataStart); - var x = i % GPU.tilemap.WIDTH; - var y = (i / GPU.tilemap.WIDTH) | 0; - this.drawTile(tileData, x * 8, y * 8, buffer, 256); - } - - var wx = this.deviceram(this.WX) - 7; - var wy = this.deviceram(this.WY); - for (var x = Math.max(0, -wx); x < Math.min(Screen.physics.WIDTH, Screen.physics.WIDTH - wx); x++) { - for (var y = Math.max(0, -wy); y < Math.min(Screen.physics.HEIGHT, Screen.physics.HEIGHT - wy); y++) { - var color = buffer[(x & 255) + (y & 255) * 256]; - this.drawPixel(x + wx, y + wy, color); - } - } -}; - -GPU.prototype.drawPixel = function(x, y, color) { - this.buffer[y * 160 + x] = color; -}; - -GPU.prototype.getPixel = function(x, y) { - return this.buffer[y * 160 + x]; -}; - -// Get the palette mapping from a given palette byte as stored in memory -// A palette will map a tile color to a final palette color index -// used with Screen.colors to get a shade of grey -GPU.getPalette = function(paletteByte) { - var palette = []; - for (var i = 0; i < 8; i += 2) { - var shade = (paletteByte & (3 << i)) >> i; - palette.push(shade); - } - return palette; -}; - -GameboyJS.GPU = GPU; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Screen device -var Screen = function(canvas, pixelSize) { - this.context = canvas.getContext('2d'); - this.canvas = canvas; - this.pixelSize = pixelSize || 1; - this.initImageData(); -}; - -Screen.colors = [ - 0xFF, - 0xAA, - 0x55, - 0x00 -]; - -Screen.physics = { - WIDTH : 160, - HEIGHT : 144, - FREQUENCY: 60 -}; - -Screen.prototype.setPixelSize = function(pixelSize) { - this.pixelSize = pixelSize; - this.initImageData(); -}; - -Screen.prototype.initImageData = function() { - this.canvas.width = Screen.physics.WIDTH * this.pixelSize; - this.canvas.height = Screen.physics.HEIGHT * this.pixelSize; - this.imageData = this.context.createImageData(this.canvas.width, this.canvas.height); -}; - -Screen.prototype.clearScreen = function() { - this.context.fillStyle = '#FFF'; - this.context.fillRect(0, 0, Screen.physics.WIDTH * this.pixelSize, Screen.physics.HEIGHT * this.pixelSize); -}; - -Screen.prototype.fillImageData = function(buffer) { - for (var y = 0; y < Screen.physics.HEIGHT; y++) { - for (var py = 0; py < this.pixelSize; py++) { - var _y = y * this.pixelSize + py; - for (var x = 0; x < Screen.physics.WIDTH; x++) { - for (var px = 0; px < this.pixelSize; px++) { - var offset = _y * this.canvas.width + (x * this.pixelSize + px); - var v = Screen.colors[buffer[y * Screen.physics.WIDTH + x]]; - this.imageData.data[offset * 4] = v; - this.imageData.data[offset * 4 + 1] = v; - this.imageData.data[offset * 4 + 2] = v; - this.imageData.data[offset * 4 + 3] = 255; - } - } - } - } -}; - -Screen.prototype.render = function(buffer) { - this.fillImageData(buffer); - this.context.putImageData(this.imageData, 0, 0); -}; - -GameboyJS.Screen = Screen; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// This exception should be thrown whenever a critical feature that -// has not been implemented is requested -function UnimplementedException(message, fatal) { - this.message = message; - this.name = UnimplementedException; - if (fatal === undefined) { - fatal = true; - } - this.fatal = fatal; -} -GameboyJS.UnimplementedException = UnimplementedException; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Object for mapping the cartridge RAM -var ExtRam = function() { - this.extRam = null; - this.ramSize = 0; - this.ramBank = 0; -}; - -ExtRam.prototype.loadRam = function(game, size) { - this.gameName = game; - - this.ramSize = size; - this.ramBanksize = this.ramSize >= 0x2000 ? 8192 : 2048; - - var key = this.getStorageKey(); - var data = localStorage.getItem(key); - if (data == null) { - this.extRam = Array.apply(null, new Array(this.ramSize)).map(function(){return 0;}); - } else { - this.extRam = JSON.parse(data); - if (this.extRam.length != size) { - console.error('Found RAM data but not matching expected size.'); - } - } -}; - -ExtRam.prototype.setRamBank = function(bank) { - this.ramBank = bank; -}; - -ExtRam.prototype.manageWrite = function(offset, value) { - this.extRam[this.ramBank * 8192 + offset] = value; -}; - -ExtRam.prototype.manageRead = function(offset) { - return this.extRam[this.ramBank * 8192 + offset]; -}; - -ExtRam.prototype.getStorageKey = function() { - return this.gameName + '_EXTRAM';; -}; -// Actually save the RAM in the physical storage (localStorage) -ExtRam.prototype.saveRamData = function() { - localStorage.setItem(this.getStorageKey(), JSON.stringify(this.extRam)); -}; -GameboyJS.ExtRam = ExtRam; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// This is the default buttons mapping for the Gamepad -// It's optimized for the XBOX pad -// -// Any other mapping can be provided as a constructor argument of the Gamepad object -// An alternative mapping should be an object with keys being the indexes -// of the gamepad buttons and values the normalized gameboy button names -var xboxMapping = { - 0: 'UP', - 1: 'DOWN', - 2: 'LEFT', - 3: 'RIGHT', - 4: 'START', - 5: 'SELECT', - 11: 'A', - 12: 'B' -}; - -// Gamepad listener -// Communication layer between the Gamepad API and the Input class -// Any physical controller can be used but the mapping should be provided -// in order to get an optimal layout of the buttons (see above) -var Gamepad = function(mapping) { - this.gamepad = null; - this.state = {A:0,B:0,START:0,SELECT:0,LEFT:0,RIGHT:0,UP:0,DOWN:0}; - this.pullInterval = null; - this.buttonMapping = mapping || xboxMapping; -}; - -// Initialize the keyboard listeners and set up the callbacks -// for button press / release -Gamepad.prototype.init = function(onPress, onRelease) { - this.onPress = onPress; - this.onRelease = onRelease; - - var self = this; - window.addEventListener('gamepadconnected', function(e) { - self.gamepad = e.gamepad; - self.activatePull(); - }); - window.addEventListener('gamepaddisconnected', function(e) { - self.gamepad = null; - self.deactivatePull(); - }); -}; - -Gamepad.prototype.activatePull = function() { - this.deactivatePull(); - this.pullInterval = setInterval(this.pullState.bind(this), 100); -}; - -Gamepad.prototype.deactivatePull = function() { - clearInterval(this.pullInterval); -}; - -// Check the state of the current gamepad in order to detect any press/release action -Gamepad.prototype.pullState = function() { - for (var index in this.buttonMapping) { - var button = this.buttonMapping[index]; - var oldState = this.state[button]; - this.state[button] = this.gamepad.buttons[index].pressed; - - if (this.state[button] == 1 && oldState == 0) { - this.managePress(button); - } else if (this.state[button] == 0 && oldState == 1) { - this.manageRelease(button); - } - } -}; - -Gamepad.prototype.managePress = function(key) { - this.onPress(key); -}; - -Gamepad.prototype.manageRelease = function(key) { - this.onRelease(key); -}; - -GameboyJS.Gamepad = Gamepad; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// The Input management system -// -// The pressKey() and releaseKey() functions should be called by a device class -// like GameboyJS.Keyboard after a physical button trigger event -// -// They rely on the name of the original buttons as parameters (see Input.keys) -var Input = function(cpu, pad) { - this.cpu = cpu; - this.memory = cpu.memory; - this.P1 = 0xFF00; - this.state = 0; - - pad.init(this.pressKey.bind(this), this.releaseKey.bind(this)); -}; - -Input.keys = { - START: 0x80, - SELECT: 0x40, - B: 0x20, - A: 0x10, - DOWN: 0x08, - UP: 0x04, - LEFT: 0x02, - RIGHT: 0x01 -}; - -Input.prototype.pressKey = function(key) { - this.state |= Input.keys[key]; - - this.cpu.requestInterrupt(GameboyJS.CPU.INTERRUPTS.HILO); -}; - -Input.prototype.releaseKey = function(key) { - var mask = 0xFF - Input.keys[key]; - this.state &= mask; -}; - -Input.prototype.update = function() { - var value = this.memory.rb(this.P1); - value = ((~value) & 0x30); // invert the value so 1 means 'active' - if (value & 0x10) { // direction keys listened - value |= (this.state & 0x0F); - } else if (value & 0x20) { // action keys listened - value |= ((this.state & 0xF0) >> 4); - } else if ((value & 0x30) == 0) { // no keys listened - value &= 0xF0; - } - - value = ((~value) & 0x3F); // invert back - this.memory[this.P1] = value; -}; -GameboyJS.Input = Input; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Keyboard listener -// Does the mapping between the keyboard and the Input class -var Keyboard = function() {}; - -// Initialize the keyboard listeners and set up the callbacks -// for button press / release -Keyboard.prototype.init = function(onPress, onRelease) { - this.onPress = onPress; - this.onRelease = onRelease; - - var self = this; - document.addEventListener('keydown', function(e) { - self.managePress(e.keyCode); - }); - document.addEventListener('keyup', function(e) { - self.manageRelease(e.keyCode); - }); -} - -Keyboard.prototype.managePress = function(keycode) { - var key = this.translateKey(keycode); - if (key) { - this.onPress(key); - } -}; - -Keyboard.prototype.manageRelease = function(keycode) { - var key = this.translateKey(keycode); - if (key) { - this.onRelease(key); - } -}; - -// Transform a keyboard keycode into a key of the Input.keys object -Keyboard.prototype.translateKey = function(keycode) { - var key = null; - switch (keycode) { - case 71: // G - key = 'A'; - break; - case 66: // B - key = 'B'; - break; - case 72: // H - key = 'START'; - break; - case 78: // N - key = 'SELECT'; - break; - case 37: // left - key = 'LEFT'; - break; - case 38: // up - key = 'UP'; - break; - case 39: // right - key = 'RIGHT'; - break; - case 40: // down - key = 'DOWN'; - break; - } - - return key; -}; -GameboyJS.Keyboard = Keyboard; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// List of CPU operations -// Most operations have been factorized here to limit code redundancy -// -// How to read operations: -// Uppercase letters qualify the kind of operation (LD = LOAD, INC = INCREMENT, etc.) -// Lowercase letters are used to hint parameters : -// r = register, n = 1 memory byte, sp = sp register, -// a = suffix for memory address, i = bit index -// Example : LDrrar = LOAD operation with two-registers memory address -// as first parameter and one register value as second -// -// Underscore-prefixed functions are here to delegate the logic between similar operations, -// they should not be called from outside -// -// It's up to each operation to update the CPU clock -var ops = { - LDrrnn: function(p, r1, r2) {p.wr(r2, p.memory.rb(p.r.pc));p.wr(r1, p.memory.rb(p.r.pc+1)); p.r.pc+=2;p.clock.c += 12;}, - LDrrar: function(p, r1, r2, r3) {ops._LDav(p, GameboyJS.Util.getRegAddr(p, r1, r2), p.r[r3]);p.clock.c += 8;}, - LDrrra: function(p, r1, r2, r3) {p.wr(r1, p.memory.rb(GameboyJS.Util.getRegAddr(p, r2, r3)));p.clock.c += 8;}, - LDrn: function(p, r1) {p.wr(r1, p.memory.rb(p.r.pc++));p.clock.c += 8;}, - LDrr: function(p, r1, r2) {p.wr(r1, p.r[r2]);p.clock.c += 4;}, - LDrar: function(p, r1, r2) {p.memory.wb(p.r[r1]+0xFF00, p.r[r2]);p.clock.c += 8;}, - LDrra: function(p, r1, r2) {p.wr(r1, p.memory.rb(p.r[r2]+0xFF00));p.clock.c += 8;}, - LDspnn: function(p) {p.wr('sp', (p.memory.rb(p.r.pc + 1) << 8) + p.memory.rb(p.r.pc));p.r.pc+=2;p.clock.c += 12;}, - LDsprr: function(p, r1, r2) {p.wr('sp', GameboyJS.Util.getRegAddr(p, r1, r2));p.clock.c += 8;}, - LDnnar: function(p, r1) {var addr=(p.memory.rb(p.r.pc + 1) << 8) + p.memory.rb(p.r.pc);p.memory.wb(addr,p.r[r1]);p.r.pc+=2; p.clock.c += 16;}, - LDrnna: function(p, r1) {var addr=(p.memory.rb(p.r.pc + 1) << 8) + p.memory.rb(p.r.pc);p.wr(r1, p.memory.rb(addr));p.r.pc+=2; p.clock.c += 16;}, - LDrrspn:function(p, r1, r2) {var rel = p.memory.rb(p.r.pc++);rel=GameboyJS.Util.getSignedValue(rel);var val=p.r.sp + rel; - var c = (p.r.sp&0xFF) + (rel&0xFF) > 0xFF;var h = (p.r.sp & 0xF) + (rel & 0xF) > 0xF;val &= 0xFFFF; - var f = 0; if(h)f|=0x20;if(c)f|=0x10;p.wr('F', f); - p.wr(r1, val >> 8);p.wr(r2, val&0xFF); - p.clock.c+=12;}, - LDnnsp: function(p) {var addr = p.memory.rb(p.r.pc++) + (p.memory.rb(p.r.pc++)<<8); ops._LDav(p, addr, p.r.sp & 0xFF);ops._LDav(p, addr+1, p.r.sp >> 8);p.clock.c+=20;}, - LDrran: function(p, r1, r2){var addr = GameboyJS.Util.getRegAddr(p, r1, r2);ops._LDav(p, addr, p.memory.rb(p.r.pc++));p.clock.c+=12;}, - _LDav: function(p, addr, val){p.memory.wb(addr, val);}, - LDHnar: function(p, r1){p.memory.wb(0xFF00 + p.memory.rb(p.r.pc++), p.r[r1]);p.clock.c+=12;}, - LDHrna: function(p, r1){p.wr(r1, p.memory.rb(0xFF00 + p.memory.rb(p.r.pc++)));p.clock.c+=12;}, - INCrr: function(p, r1, r2) {p.wr(r2, (p.r[r2]+1)&0xFF); if (p.r[r2] == 0) p.wr(r1, (p.r[r1]+1)&0xFF);p.clock.c += 8;}, - INCrra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);var val = (p.memory.rb(addr)+1)&0xFF;var z = val==0;var h=(p.memory.rb(addr)&0xF)+1 > 0xF; - p.memory.wb(addr, val); - p.r.F&=0x10;if(h)p.r.F|=0x20;if(z)p.r.F|=0x80; - p.clock.c+=12;}, - INCsp: function(p){p.wr('sp', p.r.sp+1); p.r.sp &= 0xFFFF; p.clock.c+=8;}, - INCr: function(p, r1) {var h = ((p.r[r1]&0xF) + 1)&0x10;p.wr(r1, (p.r[r1] + 1)&0xFF);var z = p.r[r1]==0; - p.r.F&=0x10;if(h)p.r.F|=0x20;if(z)p.r.F|=0x80; - p.clock.c += 4;}, - DECrr: function(p, r1, r2) {p.wr(r2, (p.r[r2] - 1) & 0xFF); if (p.r[r2] == 0xFF) p.wr(r1, (p.r[r1] - 1)&0xFF);p.clock.c += 8;}, - DECsp: function(p){p.wr('sp', p.r.sp-1); p.r.sp &= 0xFFFF; p.clock.c+=8;}, - DECr: function(p, r1) {var h = (p.r[r1]&0xF) < 1;p.wr(r1, (p.r[r1] - 1) & 0xFF);var z = p.r[r1]==0; - p.r.F&=0x10;p.r.F|=0x40;if(h)p.r.F|=0x20;if(z)p.r.F|=0x80; - p.clock.c += 4;}, - DECrra: function(p, r1, r2){var addr = GameboyJS.Util.getRegAddr(p, r1, r2);var val = (p.memory.rb(addr)-1)&0xFF;var z = val==0;var h=(p.memory.rb(addr)&0xF) < 1; - p.memory.wb(addr, val); - p.r.F&=0x10;p.r.F|=0x40;if(h)p.r.F|=0x20;if(z)p.r.F|=0x80; - p.clock.c+=12;}, - ADDrr: function(p, r1, r2) {var n = p.r[r2];ops._ADDrn(p, r1, n); p.clock.c += 4;}, - ADDrn: function(p, r1) {var n = p.memory.rb(p.r.pc++);ops._ADDrn(p, r1, n); p.clock.c+=8;}, - _ADDrn: function(p, r1, n) {var h=((p.r[r1]&0xF)+(n&0xF))&0x10;p.wr(r1, p.r[r1]+n);var c=p.r[r1]&0x100;p.r[r1]&=0xFF; - var f = 0;if (p.r[r1]==0)f|=0x80;if (h)f|=0x20;if (c)f|=0x10;p.wr('F', f);}, - ADDrrrr:function(p, r1, r2, r3, r4) {ops._ADDrrn(p, r1, r2, (p.r[r3]<<8) + p.r[r4]); p.clock.c+=8;}, - ADDrrsp:function(p, r1, r2) {ops._ADDrrn(p, r1, r2, p.r.sp); p.clock.c += 8;}, - ADDspn: function(p) {var v = p.memory.rb(p.r.pc++);v = GameboyJS.Util.getSignedValue(v); - var c = ((p.r.sp&0xFF) + (v&0xFF)) > 0xFF; var h = (p.r.sp & 0xF) + (v&0xF) > 0xF; - var f = 0; if(h)f|=0x20;if(c)f|=0x10;p.wr('F', f); - p.wr('sp', (p.r.sp + v) & 0xFFFF); - p.clock.c+=16;}, - _ADDrrn:function(p, r1, r2, n) {var v1 = (p.r[r1]<<8) + p.r[r2];var v2 = n; - var res = v1 + v2;var c = res&0x10000;var h = ((v1&0xFFF) + (v2&0xFFF))&0x1000;var z = p.r.F&0x80; - res&=0xFFFF;p.r[r2]=res&0xFF;res=res>>8;p.r[r1]=res&0xFF; - var f=0;if(z)f|=0x80;if(h)f|=0x20;if(c)f|=0x10;p.r.F=f;}, - ADCrr: function(p, r1, r2) {var n = p.r[r2]; ops._ADCrn(p, r1, n); p.clock.c += 4;}, - ADCrn: function(p, r1) {var n = p.memory.rb(p.r.pc++); ops._ADCrn(p, r1, n); p.clock.c += 8;}, - _ADCrn: function(p, r1, n) { - var c = p.r.F&0x10?1:0;var h=((p.r[r1]&0xF)+(n&0xF)+c)&0x10; - p.wr(r1, p.r[r1]+n+c);c=p.r[r1]&0x100;p.r[r1]&=0xFF; - var f = 0;if (p.r[r1]==0)f|=0x80;if (h)f|=0x20;if (c)f|=0x10;p.r.F=f;}, - ADCrrra:function(p, r1, r2, r3) {var n = p.memory.rb(GameboyJS.Util.getRegAddr(p, r2, r3)); ops._ADCrn(p, r1, n); p.clock.c += 8;}, - ADDrrra:function(p, r1, r2, r3) {var v = p.memory.rb(GameboyJS.Util.getRegAddr(p, r2, r3));var h=((p.r[r1]&0xF)+(v&0xF))&0x10;p.wr(r1, p.r[r1]+v);var c=p.r[r1]&0x100;p.r[r1]&=0xFF; - var f = 0;if (p.r[r1]==0)f|=0x80;if (h)f|=0x20;if (c)f|=0x10;p.wr('F', f); - p.clock.c += 8;}, - SUBr: function(p, r1) {var n = p.r[r1];ops._SUBn(p, n);p.clock.c += 4;}, - SUBn: function(p) {var n = p.memory.rb(p.r.pc++);ops._SUBn(p, n);p.clock.c += 8;}, - SUBrra: function(p, r1, r2) {var n = p.memory.rb(GameboyJS.Util.getRegAddr(p, r1, r2));ops._SUBn(p, n);p.clock.c+=8;}, - _SUBn: function(p, n) {var c = p.r.A < n;var h = (p.r.A&0xF) < (n&0xF); - p.wr('A', p.r.A - n);p.r.A&=0xFF; var z = p.r.A==0; - var f = 0x40;if (z)f|=0x80;if (h)f|=0x20;if (c)f|=0x10;p.wr('F', f);}, - SBCn: function(p) {var n = p.memory.rb(p.r.pc++); ops._SBCn(p, n); p.clock.c += 8;}, - SBCr: function(p, r1) {var n = p.r[r1]; ops._SBCn(p, n); p.clock.c += 4;}, - SBCrra: function(p, r1, r2) {var v = p.memory.rb((p.r[r1] << 8) + p.r[r2]); ops._SBCn(p, v); p.clock.c += 8;}, - _SBCn: function(p, n) {var carry = p.r.F&0x10 ? 1 : 0; - var c = p.r.A < n + carry;var h = (p.r.A&0xF) < (n&0xF) + carry; - p.wr('A', p.r.A - n - carry); p.r.A&=0xFF; var z = p.r.A == 0; - var f = 0x40;if (z)f|=0x80;if (h)f|=0x20;if (c)f|=0x10;p.r.F=f;}, - ORr: function(p, r1) {p.r.A|=p.r[r1];p.r.F=(p.r.A==0)?0x80:0x00;p.clock.c += 4;}, - ORn: function(p) {p.r.A|=p.memory.rb(p.r.pc++);p.r.F=(p.r.A==0)?0x80:0x00;p.clock.c += 8;}, - ORrra: function(p, r1, r2) {p.r.A|=p.memory.rb((p.r[r1] << 8)+ p.r[r2]);p.r.F=(p.r.A==0)?0x80:0x00;p.clock.c += 8;}, - ANDr: function(p, r1) {p.r.A&=p.r[r1];p.r.F=(p.r.A==0)?0xA0:0x20;p.clock.c += 4;}, - ANDn: function(p) {p.r.A&=p.memory.rb(p.r.pc++);p.r.F=(p.r.A==0)?0xA0:0x20;p.clock.c += 8;}, - ANDrra: function(p, r1, r2) {p.r.A&=p.memory.rb(GameboyJS.Util.getRegAddr(p, r1, r2));p.r.F=(p.r.A==0)?0xA0:0x20;p.clock.c += 8;}, - XORr: function(p, r1) {p.r.A^=p.r[r1];p.r.F=(p.r.A==0)?0x80:0x00;p.clock.c += 4;}, - XORn: function(p) {p.r.A^=p.memory.rb(p.r.pc++);p.r.F=(p.r.A==0)?0x80:0x00;p.clock.c += 8;}, - XORrra: function(p, r1, r2) {p.r.A^=p.memory.rb((p.r[r1] << 8)+ p.r[r2]);p.r.F=(p.r.A==0)?0x80:0x00;p.clock.c += 8;}, - CPr: function(p, r1) {var n = p.r[r1];ops._CPn(p, n); p.clock.c += 4;}, - CPn: function(p) {var n =p.memory.rb(p.r.pc++);ops._CPn(p, n);p.clock.c+=8;}, - CPrra: function(p, r1, r2) {var n = p.memory.rb(GameboyJS.Util.getRegAddr(p, r1, r2));ops._CPn(p, n);p.clock.c+=8;}, - _CPn: function(p, n) { - var c = p.r.A < n;var z = p.r.A == n;var h = (p.r.A&0xF) < (n&0xF); - var f = 0x40;if(z)f+=0x80;if (h)f+=0x20;if (c)f+=0x10;p.r.F=f;}, - RRCr: function(p, r1) {p.r.F=0;var out=p.r[r1] & 0x01;if(out)p.r.F|=0x10;p.r[r1]=(p.r[r1]>>1)|(out*0x80);if(p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - RRCrra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);p.r.F=0;var out=p.memory.rb(addr)&0x01;if(out)p.r.F|=0x10;p.memory.wb(addr, (p.memory.rb(addr)>>1)|(out*0x80));if(p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - RLCr: function(p, r1) {p.r.F=0;var out=p.r[r1]&0x80?1:0;if(out)p.r.F|=0x10;p.r[r1]=((p.r[r1]<<1)+out)&0xFF;if(p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - RLCrra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);p.r.F=0;var out=p.memory.rb(addr)&0x80?1:0;if(out)p.r.F|=0x10;p.memory.wb(addr, ((p.memory.rb(addr)<<1)+out)&0xFF);if(p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - RLr: function(p, r1) {var c=(p.r.F&0x10)?1:0;p.r.F=0;var out=p.r[r1]&0x80;out?p.r.F|=0x10:p.r.F&=0xEF;p.r[r1]=((p.r[r1]<<1)+c)&0xFF;if(p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - RLrra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);var c=(p.r.F&0x10)?1:0;p.r.F=0;var out=p.memory.rb(addr)&0x80;out?p.r.F|=0x10:p.r.F&=0xEF;p.memory.wb(addr,((p.memory.rb(addr)<<1)+c)&0xFF);if(p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - RRr: function(p, r1) {var c=(p.r.F&0x10)?1:0;p.r.F=0;var out=p.r[r1]&0x01;out?p.r.F|=0x10:p.r.F&=0xEF;p.r[r1]=(p.r[r1]>>1)|(c*0x80);if(p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - RRrra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);var c=(p.r.F&0x10)?1:0;p.r.F=0;var out=p.memory.rb(addr)&0x01;out?p.r.F|=0x10:p.r.F&=0xEF;p.memory.wb(addr,(p.memory.rb(addr)>>1)|(c*0x80));if(p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - SRAr: function(p, r1) {p.r.F = 0;if (p.r[r1]&0x01)p.r.F|=0x10;var msb=p.r[r1]&0x80;p.r[r1]=(p.r[r1]>>1)|msb;if (p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - SRArra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);p.r.F = 0;if (p.memory.rb(addr)&0x01)p.r.F|=0x10;var msb=p.memory.rb(addr)&0x80;p.memory.wb(addr, (p.memory.rb(addr)>>1)|msb);if (p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - SLAr: function(p, r1) {p.r.F = 0;if (p.r[r1]&0x80)p.r.F|=0x10;p.r[r1]=(p.r[r1]<<1)&0xFF;if (p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - SLArra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);p.r.F = 0;if (p.memory.rb(addr)&0x80)p.r.F|=0x10;p.memory.wb(addr, (p.memory.rb(addr)<<1)&0xFF);if (p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - SRLr: function(p, r1) {p.r.F = 0;if (p.r[r1]&0x01)p.r.F|=0x10;p.r[r1]=p.r[r1]>>1;if (p.r[r1]==0)p.r.F|=0x80;p.clock.c+=4;}, - SRLrra: function(p, r1, r2) {var addr = GameboyJS.Util.getRegAddr(p, r1, r2);p.r.F = 0;if (p.memory.rb(addr)&0x01)p.r.F|=0x10;p.memory.wb(addr, p.memory.rb(addr)>>1);if (p.memory.rb(addr)==0)p.r.F|=0x80;p.clock.c+=12;}, - BITir: function(p, i, r1) {var mask=1<> 4) | ((n&0x0F) << 4);}, - JPnn: function(p) {p.wr('pc', (p.memory.rb(p.r.pc+1) << 8) + p.memory.rb(p.r.pc));p.clock.c += 16;}, - JRccn: function(p, cc) {if (GameboyJS.Util.testFlag(p, cc)){var v=p.memory.rb(p.r.pc++);v=GameboyJS.Util.getSignedValue(v);p.r.pc += v;p.clock.c+=4;}else{p.r.pc++;}p.clock.c += 8;}, - JPccnn: function(p, cc) {if (GameboyJS.Util.testFlag(p, cc)){p.wr('pc', (p.memory.rb(p.r.pc+1) << 8) + p.memory.rb(p.r.pc));p.clock.c+=4;}else{p.r.pc+=2;}p.clock.c += 12;}, - JPrr: function(p, r1, r2) {p.r.pc = (p.r[r1] << 8) + p.r[r2];p.clock.c += 4;}, - JRn: function(p) {var v=p.memory.rb(p.r.pc++);v=GameboyJS.Util.getSignedValue(v);p.r.pc += v;p.clock.c += 12;}, - PUSHrr: function(p, r1, r2) {p.wr('sp', p.r.sp-1);p.memory.wb(p.r.sp, p.r[r1]);p.wr('sp', p.r.sp-1);p.memory.wb(p.r.sp, p.r[r2]);p.clock.c+=16;}, - POPrr: function(p, r1, r2) {p.wr(r2, p.memory.rb(p.r.sp));p.wr('sp', p.r.sp+1);p.wr(r1, p.memory.rb(p.r.sp));p.wr('sp', p.r.sp+1);p.clock.c+=12;}, - RSTn: function(p, n) {p.wr('sp', p.r.sp-1);p.memory.wb(p.r.sp,p.r.pc>>8);p.wr('sp', p.r.sp-1);p.memory.wb(p.r.sp,p.r.pc&0xFF);p.r.pc=n;p.clock.c+=16;}, - RET: function(p) {p.r.pc = p.memory.rb(p.r.sp);p.wr('sp', p.r.sp+1);p.r.pc+=p.memory.rb(p.r.sp)<<8;p.wr('sp', p.r.sp+1);p.clock.c += 16;}, - RETcc: function(p, cc) {if (GameboyJS.Util.testFlag(p, cc)){p.r.pc = p.memory.rb(p.r.sp);p.wr('sp', p.r.sp+1);p.r.pc+=p.memory.rb(p.r.sp)<<8;p.wr('sp', p.r.sp+1);p.clock.c+=12;}p.clock.c+=8;}, - CALLnn: function(p) {ops._CALLnn(p); p.clock.c+=24;}, - CALLccnn:function(p, cc) {if (GameboyJS.Util.testFlag(p, cc)){ops._CALLnn(p);p.clock.c+=12;}else{p.r.pc+=2;}p.clock.c+=12; }, - _CALLnn:function(p){p.wr('sp', p.r.sp - 1); p.memory.wb(p.r.sp, ((p.r.pc+2)&0xFF00)>>8); - p.wr('sp', p.r.sp - 1); p.memory.wb(p.r.sp, (p.r.pc+2)&0x00FF); - var j=p.memory.rb(p.r.pc)+(p.memory.rb(p.r.pc+1)<<8);p.r.pc=j;}, - CPL: function(p) {p.wr('A', (~p.r.A)&0xFF);p.r.F|=0x60,p.clock.c += 4;}, - CCF: function(p) {p.r.F&=0x9F;p.r.F&0x10?p.r.F&=0xE0:p.r.F|=0x10;p.clock.c += 4;}, - SCF: function(p) {p.r.F&=0x9F;p.r.F|=0x10;p.clock.c+=4;}, - DAA: function(p) { - var sub = (p.r.F&0x40) ? 1 : 0; var h = (p.r.F&0x20)?1:0;var c = (p.r.F&0x10)?1:0; - if (sub) { - if (h) { - p.r.A = (p.r.A - 0x6) & 0xFF; - } - if (c) { - p.r.A -= 0x60; - } - } else { - if ((p.r.A&0xF) > 9 || h) { - p.r.A += 0x6; - } - if (p.r.A > 0x9F || c) { - p.r.A += 0x60; - } - } - if (p.r.A&0x100) c = 1; - - p.r.A &= 0xFF; - p.r.F &= 0x40;if (p.r.A == 0) p.r.F|=0x80;if (c) p.r.F|=0x10; - p.clock.c += 4; - }, - HALT: function(p) {p.halt(); p.clock.c+=4;}, - DI: function(p) {p.disableInterrupts();p.clock.c += 4;}, - EI: function(p) {p.enableInterrupts();p.clock.c += 4;}, - RETI: function(p) {p.enableInterrupts();ops.RET(p);}, - CB: function(p) {var opcode = p.memory.rb(p.r.pc++); - GameboyJS.opcodeCbmap[opcode](p); - p.clock.c+=4;} -}; -GameboyJS.cpuOps = ops; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -var defaultOptions = { - pad: {class: GameboyJS.Keyboard, mapping: null}, - zoom: 1, - romReaders: [], - statusContainerId: 'status', - gameNameContainerId: 'game-name', - errorContainerId: 'error' -}; - -// Gameboy class -// -// This object is the entry point of the application -// Will delegate user actions to the emulated devices -// and provide information where needed -var Gameboy = function(canvas, options) { - options = options || {}; - this.options = GameboyJS.Util.extend({}, defaultOptions, options); - - var cpu = new GameboyJS.CPU(this); - var screen = new GameboyJS.Screen(canvas, this.options.zoom); - var gpu = new GameboyJS.GPU(screen, cpu); - cpu.gpu = gpu; - - var pad = new this.options.pad.class(this.options.pad.mapping); - var input = new GameboyJS.Input(cpu, pad); - cpu.input = input; - - this.cpu = cpu; - this.screen = screen; - this.input = input; - this.pad = pad; - - this.createRom(this.options.romReaders); - - this.statusContainer = document.getElementById(this.options.statusContainerId) || document.createElement('div'); - this.gameNameContainer = document.getElementById(this.options.gameNameContainerId) || document.createElement('div'); - this.errorContainer = document.getElementById(this.options.errorContainerId) || document.createElement('div'); -}; - -// Create the ROM object and bind one or more readers -Gameboy.prototype.createRom = function (readers) { - var rom = new GameboyJS.Rom(this); - if (readers.length == 0) { - // add the default rom reader - var romReader = new GameboyJS.RomFileReader(); - rom.addReader(romReader); - } else { - for (var i in readers) { - if (readers.hasOwnProperty(i)) { - rom.addReader(readers[i]); - } - } - } -}; - -Gameboy.prototype.startRom = function(rom) { - this.errorContainer.classList.add('hide'); - this.cpu.reset(); - try { - this.cpu.loadRom(rom.data); - this.setStatus('Game Running :'); - this.setGameName(this.cpu.getGameName()); - this.cpu.run(); - } catch (e) { - this.handleException(e); - } -}; - -Gameboy.prototype.pause = function(value) { - if (value) { - this.setStatus('Game Paused :'); - this.cpu.pause(); - } else { - this.setStatus('Game Running :'); - this.cpu.unpause(); - } -}; - -Gameboy.prototype.error = function(message) { - this.setStatus('Error during execution'); - this.setError('An error occurred during execution:' + message); - this.cpu.stop(); -}; - -Gameboy.prototype.setStatus = function(status) { - this.statusContainer.innerHTML = status; -}; -// Display an error message -Gameboy.prototype.setError = function(message) { - this.errorContainer.classList.remove('hide'); - this.errorContainer.innerHTML = message; -}; -// Display the name of the game running -Gameboy.prototype.setGameName = function(name) { - this.gameNameContainer.innerHTML = name; -}; -Gameboy.prototype.setSoundEnabled = function(value) { - if (value) { - this.cpu.apu.connect(); - } else { - this.cpu.apu.disconnect(); - } -}; -Gameboy.prototype.setScreenZoom = function(value) { - this.screen.setPixelSize(value); -}; -Gameboy.prototype.handleException = function(e) { - if (e instanceof GameboyJS.UnimplementedException) { - if (e.fatal) { - this.error('This cartridge is not supported ('+ e.message +')'); - } else { - console.error(e.message); - } - } else { - throw e; - } -}; -GameboyJS.Gameboy = Gameboy; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Memory bank controllers - -var MBC = {}; - -// Create an MBC instance depending on the type specified in the cartridge -MBC.getMbcInstance = function(memory, type) { - var instance; - switch (type) { - case 0x00: - instance = new MBC0(memory); - break; - case 0x01: case 0x02: case 0x03: - instance = new MBC1(memory); - break; - case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: - instance = new MBC3(memory); - break; - case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: - instance = new MBC5(memory); - break; - default: - throw new GameboyJS.UnimplementedException('MBC type not supported'); - } - - return instance; -}; - -var MBC1 = function(memory) { - this.memory = memory; - this.romBankNumber = 1; - this.mode = 0; // mode 0 = ROM, mode 1 = RAM - this.ramEnabled = true; - this.extRam = new GameboyJS.ExtRam(); -}; - -MBC1.prototype.loadRam = function(game, size) { - this.extRam.loadRam(game, size); -}; - -MBC1.prototype.manageWrite = function(addr, value) { - switch (addr & 0xF000) { - case 0x0000: case 0x1000: // enable RAM - this.ramEnabled = (value & 0x0A) ? true : false; - if (this.ramEnabled) { - this.extRam.saveRamData(); - } - break; - case 0x2000: case 0x3000: // ROM bank number lower 5 bits - value &= 0x1F; - if (value == 0) value = 1; - var mask = this.mode ? 0 : 0xE0; - this.romBankNumber = (this.romBankNumber & mask) +value; - this.memory.loadRomBank(this.romBankNumber); - break; - case 0x4000: case 0x5000: // RAM bank or high bits ROM - value &= 0x03; - if (this.mode == 0) { // ROM upper bits - this.romBankNumber = (this.romBankNumber&0x1F) | (value << 5); - this.memory.loadRomBank(this.romBankNumber); - } else { // RAM bank - this.extRam.setRamBank(value); - } - break; - case 0x6000: case 0x7000: // ROM / RAM mode - this.mode = value & 1; - break; - case 0xA000: case 0xB000: - this.extRam.manageWrite(addr - 0xA000, value); - break; - } -}; -MBC1.prototype.readRam = function(addr) { - return this.extRam.manageRead(addr - 0xA000); -}; - -var MBC3 = function(memory) { - this.memory = memory; - this.romBankNumber = 1; - this.ramEnabled = true; - this.extRam = new GameboyJS.ExtRam(); -}; - -MBC3.prototype.loadRam = function(game, size) { - this.extRam.loadRam(game, size); -}; - -MBC3.prototype.manageWrite = function(addr, value) { - switch (addr & 0xF000) { - case 0x0000: case 0x1000: // enable RAM - this.ramEnabled = (value & 0x0A) ? true : false; - if (this.ramEnabled) { - this.extRam.saveRamData(); - } - break; - case 0x2000: case 0x3000: // ROM bank number - value &= 0x7F; - if (value == 0) value = 1; - this.romBankNumber = value; - this.memory.loadRomBank(this.romBankNumber); - break; - case 0x4000: case 0x5000: // RAM bank - this.extRam.setRamBank(value); - break; - case 0x6000: case 0x7000: // Latch clock data - throw new GameboyJS.UnimplementedException('cartridge clock not supported', false); - break; - case 0xA000: case 0xB000: - this.extRam.manageWrite(addr - 0xA000, value); - break; - } -}; -MBC3.prototype.readRam = function(addr) { - return this.extRam.manageRead(addr - 0xA000); -}; - -// declare MBC5 for compatibility with most cartriges -// does not support rumble feature -var MBC5 = MBC3; - -// MBC0 exists for consistency and manages the no-MBC cartriges -var MBC0 = function(memory) {this.memory = memory;}; - -MBC0.prototype.manageWrite = function(addr, value) { - this.memory.loadRomBank(value); -}; -MBC0.prototype.readRam = function(addr) {return 0;}; -MBC0.prototype.loadRam = function() {}; - -GameboyJS.MBC = MBC; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Memory unit -var Memory = function(cpu) { - this.MEM_SIZE = 65536; // 64KB - - this.MBCtype = 0; - this.banksize = 0x4000; - this.rom = null; - this.mbc = null; - this.cpu = cpu; -}; - -Memory.addresses = { - VRAM_START : 0x8000, - VRAM_END : 0x9FFF, - - EXTRAM_START : 0xA000, - EXTRAM_END : 0xBFFF, - - OAM_START : 0xFE00, - OAM_END : 0xFE9F, - - DEVICE_START: 0xFF00, - DEVICE_END: 0xFF7F -}; - -// Memory can be accessed as an Array -Memory.prototype = new Array(); - -Memory.prototype.reset = function() { - this.length = this.MEM_SIZE; - for (var i = Memory.addresses.VRAM_START; i <= Memory.addresses.VRAM_END; i++) { - this[i] = 0; - } - for (var i = Memory.addresses.DEVICE_START; i <= Memory.addresses.DEVICE_END; i++) { - this[i] = 0; - } - this[0xFFFF] = 0; -}; - -Memory.prototype.setRomData = function(data) { - this.rom = data; - this.loadRomBank(0); - this.mbc = GameboyJS.MBC.getMbcInstance(this, this[0x147]); - this.loadRomBank(1); - this.mbc.loadRam(this.cpu.getGameName(), this.cpu.getRamSize()); -}; - -Memory.prototype.loadRomBank = function(index) { - var start = index ? 0x4000 : 0x0; - var romStart = index * 0x4000; - for (var i = 0; i < this.banksize; i++) { - this[i + start] = this.rom[romStart + i]; - } -}; - -// Video ram accessor -Memory.prototype.vram = function(address) { - if (address < Memory.addresses.VRAM_START || address > Memory.addresses.VRAM_END) { - throw 'VRAM access in out of bounds address ' + address; - } - - return this[address]; -}; - -// OAM ram accessor -Memory.prototype.oamram = function(address) { - if (address < Memory.addresses.OAM_START || address > Memory.addresses.OAM_END) { - throw 'OAMRAM access in out of bounds address ' + address; - } - - return this[address]; -}; - -// Device ram accessor -Memory.prototype.deviceram = function(address, value) { - if (address < Memory.addresses.DEVICERAM_START || address > Memory.addresses.DEVICERAM_END) { - throw 'Device RAM access in out of bounds address ' + address; - } - if (typeof value === "undefined") { - return this[address]; - } else { - this[address] = value; - } - -}; - -// Memory read proxy function -// Used to centralize memory read access -Memory.prototype.rb = function (addr) { - if (addr >= 0xFF10 && addr < 0xFF40) { - var mask = apuMask[addr - 0xFF10]; - return this[addr] | mask; - } - if ((addr >= 0xA000 && addr < 0xC000)) { - return this.mbc.readRam(addr); - } - return this[addr]; -}; - -// Bitmasks for audio addresses reads -var apuMask = [ -0x80,0x3F,0x00,0xFF,0xBF, // NR10-NR15 -0xFF,0x3F,0x00,0xFF,0xBF, // NR20-NR25 -0x7F,0xFF,0x9F,0xFF,0xBF, // NR30-NR35 -0xFF,0xFF,0x00,0x00,0xBF, // NR40-NR45 -0x00,0x00,0x70, // NR50-NR52 -0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Wave RAM -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -]; - -// Memory write proxy function -// Used to centralize memory writes and delegate specific behaviour -// to the correct units -Memory.prototype.wb = function(addr, value) { - if (addr < 0x8000 || (addr >= 0xA000 && addr < 0xC000)) { // MBC - this.mbc.manageWrite(addr, value); - } else if (addr >= 0xFF10 && addr <= 0xFF3F) { // sound registers - this.cpu.apu.manageWrite(addr, value); - } else if (addr == 0xFF00) { // input register - this[addr] = ((this[addr] & 0x0F) | (value & 0x30)); - } else { - this[addr] = value; - if ((addr & 0xFF00) == 0xFF00) { - if (addr == 0xFF02) { - if (value & 0x80) { - this.cpu.enableSerialTransfer(); - } - } - if (addr == 0xFF04) { - this.cpu.resetDivTimer(); - } - if (addr == 0xFF46) { // OAM DMA transfer - this.dmaTransfer(value); - } - } - } -} - -// Start a DMA transfer (OAM data from cartrige to RAM) -Memory.prototype.dmaTransfer = function(startAddressPrefix) { - var startAddress = (startAddressPrefix << 8); - for (var i = 0; i < 0xA0; i++) { - this[Memory.addresses.OAM_START + i] = this[startAddress + i]; - } -}; - -GameboyJS.Memory = Memory; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -var ops = GameboyJS.cpuOps; -// Each opcode (0 to 0xFF) is associated to a CPU operation -// CPU operations are implemented separately -// The cbmap object holds operations for CB prefixed opcodes (0xCB00 to 0xCBFF) -// Non existent opcodes are commented out and marked empty -var map = { - 0x00: function(p){p.clock.c += 4;}, - 0x01: function(p){ops.LDrrnn(p, 'B', 'C');}, - 0x02: function(p){ops.LDrrar(p, 'B', 'C', 'A');}, - 0x03: function(p){ops.INCrr(p, 'B', 'C');}, - 0x04: function(p){ops.INCr(p, 'B');}, - 0x05: function(p){ops.DECr(p, 'B');}, - 0x06: function(p){ops.LDrn(p, 'B');}, - 0x07: function(p){var out=p.r.A & 0x80?1:0; out ? p.r.F=0x10:p.r.F=0; p.wr('A', ((p.r.A<<1)+out)&0xFF);p.clock.c+=4;}, - 0x08: function(p){ops.LDnnsp(p);}, - 0x09: function(p){ops.ADDrrrr(p, 'H', 'L', 'B', 'C');}, - 0x0A: function(p){ops.LDrrra(p, 'A', 'B', 'C');}, - 0x0B: function(p){ops.DECrr(p, 'B', 'C');}, - 0x0C: function(p){ops.INCr(p, 'C');}, - 0x0D: function(p){ops.DECr(p, 'C');}, - 0x0E: function(p){ops.LDrn(p, 'C');}, - 0x0F: function(p){var out=p.r.A & 0x01; out ? p.r.F=0x10:p.r.F=0; p.wr('A', (p.r.A>>1)|(out*0x80));p.clock.c+=4;}, - - 0x10: function(p){p.r.pc++;p.clock.c+=4;}, - 0x11: function(p){ops.LDrrnn(p, 'D', 'E');}, - 0x12: function(p){ops.LDrrar(p, 'D', 'E', 'A');}, - 0x13: function(p){ops.INCrr(p, 'D', 'E');}, - 0x14: function(p){ops.INCr(p, 'D');}, - 0x15: function(p){ops.DECr(p, 'D');}, - 0x16: function(p){ops.LDrn(p, 'D');}, - 0x17: function(p){var c = (p.r.F&0x10)?1:0;var out=p.r.A & 0x80?1:0; out ? p.r.F=0x10:p.r.F=0; p.wr('A',((p.r.A<<1)+c)&0xFF);p.clock.c+=4;}, - 0x18: function(p){ops.JRn(p);}, - 0x19: function(p){ops.ADDrrrr(p, 'H', 'L', 'D', 'E');}, - 0x1A: function(p){ops.LDrrra(p, 'A', 'D', 'E');}, - 0x1B: function(p){ops.DECrr(p, 'D', 'E');}, - 0x1C: function(p){ops.INCr(p, 'E');}, - 0x1D: function(p){ops.DECr(p, 'E');}, - 0x1E: function(p){ops.LDrn(p, 'E');}, - 0x1F: function(p){var c = (p.r.F&0x10)?1:0;var out=p.r.A & 0x01; out ? p.r.F=0x10:p.r.F=0; p.wr('A', (p.r.A>>1)|(c*0x80));p.clock.c+=4;}, - - 0x20: function(p){ops.JRccn(p, 'NZ');}, - 0x21: function(p){ops.LDrrnn(p, 'H', 'L');}, - 0x22: function(p){ops.LDrrar(p, 'H', 'L', 'A');ops.INCrr(p, 'H', 'L');p.clock.c -= 8;}, - 0x23: function(p){ops.INCrr(p, 'H', 'L');}, - 0x24: function(p){ops.INCr(p, 'H');}, - 0x25: function(p){ops.DECr(p, 'H');}, - 0x26: function(p){ops.LDrn(p, 'H');}, - 0x27: function(p){ops.DAA(p);}, - 0x28: function(p){ops.JRccn(p, 'Z');}, - 0x29: function(p){ops.ADDrrrr(p, 'H', 'L', 'H', 'L');}, - 0x2A: function(p){ops.LDrrra(p, 'A', 'H', 'L');ops.INCrr(p, 'H', 'L');p.clock.c -= 8;}, - 0x2B: function(p){ops.DECrr(p, 'H', 'L');}, - 0x2C: function(p){ops.INCr(p, 'L');}, - 0x2D: function(p){ops.DECr(p, 'L');}, - 0x2E: function(p){ops.LDrn(p, 'L');}, - 0x2F: function(p){ops.CPL(p);}, - - 0x30: function(p){ops.JRccn(p, 'NC');}, - 0x31: function(p){ops.LDspnn(p);}, - 0x32: function(p){ops.LDrrar(p, 'H', 'L', 'A');ops.DECrr(p, 'H', 'L');p.clock.c -= 8;}, - 0x33: function(p){ops.INCsp(p);}, - 0x34: function(p){ops.INCrra(p, 'H', 'L');}, - 0x35: function(p){ops.DECrra(p, 'H', 'L');}, - 0x36: function(p){ops.LDrran(p, 'H', 'L');}, - 0x37: function(p){ops.SCF(p);}, - 0x38: function(p){ops.JRccn(p, 'C');}, - 0x39: function(p){ops.ADDrrsp(p, 'H', 'L');}, - 0x3A: function(p){ops.LDrrra(p, 'A', 'H', 'L');ops.DECrr(p, 'H', 'L');p.clock.c -= 8;}, - 0x3B: function(p){ops.DECsp(p);}, - 0x3C: function(p){ops.INCr(p, 'A');}, - 0x3D: function(p){ops.DECr(p, 'A');}, - 0x3E: function(p){ops.LDrn(p, 'A');}, - 0x3F: function(p){ops.CCF(p);}, - - 0x40: function(p){ops.LDrr(p, 'B', 'B');}, - 0x41: function(p){ops.LDrr(p, 'B', 'C');}, - 0x42: function(p){ops.LDrr(p, 'B', 'D');}, - 0x43: function(p){ops.LDrr(p, 'B', 'E');}, - 0x44: function(p){ops.LDrr(p, 'B', 'H');}, - 0x45: function(p){ops.LDrr(p, 'B', 'L');}, - 0x46: function(p){ops.LDrrra(p, 'B', 'H', 'L');}, - 0x47: function(p){ops.LDrr(p, 'B', 'A');}, - 0x48: function(p){ops.LDrr(p, 'C', 'B');}, - 0x49: function(p){ops.LDrr(p, 'C', 'C');}, - 0x4A: function(p){ops.LDrr(p, 'C', 'D');}, - 0x4B: function(p){ops.LDrr(p, 'C', 'E');}, - 0x4C: function(p){ops.LDrr(p, 'C', 'H');}, - 0x4D: function(p){ops.LDrr(p, 'C', 'L');}, - 0x4E: function(p){ops.LDrrra(p, 'C', 'H', 'L');}, - 0x4F: function(p){ops.LDrr(p, 'C', 'A');}, - - 0x50: function(p){ops.LDrr(p, 'D', 'B');}, - 0x51: function(p){ops.LDrr(p, 'D', 'C');}, - 0x52: function(p){ops.LDrr(p, 'D', 'D');}, - 0x53: function(p){ops.LDrr(p, 'D', 'E');}, - 0x54: function(p){ops.LDrr(p, 'D', 'H');}, - 0x55: function(p){ops.LDrr(p, 'D', 'L');}, - 0x56: function(p){ops.LDrrra(p, 'D', 'H', 'L');}, - 0x57: function(p){ops.LDrr(p, 'D', 'A');}, - 0x58: function(p){ops.LDrr(p, 'E', 'B');}, - 0x59: function(p){ops.LDrr(p, 'E', 'C');}, - 0x5A: function(p){ops.LDrr(p, 'E', 'D');}, - 0x5B: function(p){ops.LDrr(p, 'E', 'E');}, - 0x5C: function(p){ops.LDrr(p, 'E', 'H');}, - 0x5D: function(p){ops.LDrr(p, 'E', 'L');}, - 0x5E: function(p){ops.LDrrra(p, 'E', 'H', 'L');}, - 0x5F: function(p){ops.LDrr(p, 'E', 'A');}, - - 0x60: function(p){ops.LDrr(p, 'H', 'B');}, - 0x61: function(p){ops.LDrr(p, 'H', 'C');}, - 0x62: function(p){ops.LDrr(p, 'H', 'D');}, - 0x63: function(p){ops.LDrr(p, 'H', 'E');}, - 0x64: function(p){ops.LDrr(p, 'H', 'H');}, - 0x65: function(p){ops.LDrr(p, 'H', 'L');}, - 0x66: function(p){ops.LDrrra(p, 'H', 'H', 'L');}, - 0x67: function(p){ops.LDrr(p, 'H', 'A');}, - 0x68: function(p){ops.LDrr(p, 'L', 'B');}, - 0x69: function(p){ops.LDrr(p, 'L', 'C');}, - 0x6A: function(p){ops.LDrr(p, 'L', 'D');}, - 0x6B: function(p){ops.LDrr(p, 'L', 'E');}, - 0x6C: function(p){ops.LDrr(p, 'L', 'H');}, - 0x6D: function(p){ops.LDrr(p, 'L', 'L');}, - 0x6E: function(p){ops.LDrrra(p, 'L', 'H', 'L');}, - 0x6F: function(p){ops.LDrr(p, 'L', 'A');}, - - 0x70: function(p){ops.LDrrar(p, 'H', 'L', 'B');}, - 0x71: function(p){ops.LDrrar(p, 'H', 'L', 'C');}, - 0x72: function(p){ops.LDrrar(p, 'H', 'L', 'D');}, - 0x73: function(p){ops.LDrrar(p, 'H', 'L', 'E');}, - 0x74: function(p){ops.LDrrar(p, 'H', 'L', 'H');}, - 0x75: function(p){ops.LDrrar(p, 'H', 'L', 'L');}, - 0x76: function(p){ops.HALT(p);}, - 0x77: function(p){ops.LDrrar(p, 'H', 'L', 'A');}, - 0x78: function(p){ops.LDrr(p, 'A', 'B');}, - 0x79: function(p){ops.LDrr(p, 'A', 'C');}, - 0x7A: function(p){ops.LDrr(p, 'A', 'D');}, - 0x7B: function(p){ops.LDrr(p, 'A', 'E');}, - 0x7C: function(p){ops.LDrr(p, 'A', 'H');}, - 0x7D: function(p){ops.LDrr(p, 'A', 'L');}, - 0x7E: function(p){ops.LDrrra(p, 'A', 'H', 'L');}, - 0x7F: function(p){ops.LDrr(p, 'A', 'A');}, - - 0x80: function(p){ops.ADDrr(p, 'A', 'B');}, - 0x81: function(p){ops.ADDrr(p, 'A', 'C');}, - 0x82: function(p){ops.ADDrr(p, 'A', 'D');}, - 0x83: function(p){ops.ADDrr(p, 'A', 'E');}, - 0x84: function(p){ops.ADDrr(p, 'A', 'H');}, - 0x85: function(p){ops.ADDrr(p, 'A', 'L');}, - 0x86: function(p){ops.ADDrrra(p, 'A', 'H', 'L');}, - 0x87: function(p){ops.ADDrr(p, 'A', 'A');}, - 0x88: function(p){ops.ADCrr(p, 'A', 'B');}, - 0x89: function(p){ops.ADCrr(p, 'A', 'C');}, - 0x8A: function(p){ops.ADCrr(p, 'A', 'D');}, - 0x8B: function(p){ops.ADCrr(p, 'A', 'E');}, - 0x8C: function(p){ops.ADCrr(p, 'A', 'H');}, - 0x8D: function(p){ops.ADCrr(p, 'A', 'L');}, - 0x8E: function(p){ops.ADCrrra(p, 'A', 'H', 'L');}, - 0x8F: function(p){ops.ADCrr(p, 'A', 'A');}, - - 0x90: function(p){ops.SUBr(p, 'B');}, - 0x91: function(p){ops.SUBr(p, 'C');}, - 0x92: function(p){ops.SUBr(p, 'D');}, - 0x93: function(p){ops.SUBr(p, 'E');}, - 0x94: function(p){ops.SUBr(p, 'H');}, - 0x95: function(p){ops.SUBr(p, 'L');}, - 0x96: function(p){ops.SUBrra(p, 'H', 'L');}, - 0x97: function(p){ops.SUBr(p, 'A');}, - 0x98: function(p){ops.SBCr(p, 'B');}, - 0x99: function(p){ops.SBCr(p, 'C');}, - 0x9A: function(p){ops.SBCr(p, 'D');}, - 0x9B: function(p){ops.SBCr(p, 'E');}, - 0x9C: function(p){ops.SBCr(p, 'H');}, - 0x9D: function(p){ops.SBCr(p, 'L');}, - 0x9E: function(p){ops.SBCrra(p, 'H', 'L');}, - 0x9F: function(p){ops.SBCr(p, 'A');}, - - 0xA0: function(p){ops.ANDr(p, 'B');}, - 0xA1: function(p){ops.ANDr(p, 'C');}, - 0xA2: function(p){ops.ANDr(p, 'D');}, - 0xA3: function(p){ops.ANDr(p, 'E');}, - 0xA4: function(p){ops.ANDr(p, 'H');}, - 0xA5: function(p){ops.ANDr(p, 'L');}, - 0xA6: function(p){ops.ANDrra(p, 'H', 'L');}, - 0xA7: function(p){ops.ANDr(p, 'A');}, - 0xA8: function(p){ops.XORr(p, 'B');}, - 0xA9: function(p){ops.XORr(p, 'C');}, - 0xAA: function(p){ops.XORr(p, 'D');}, - 0xAB: function(p){ops.XORr(p, 'E');}, - 0xAC: function(p){ops.XORr(p, 'H');}, - 0xAD: function(p){ops.XORr(p, 'L');}, - 0xAE: function(p){ops.XORrra(p, 'H', 'L');}, - 0xAF: function(p){ops.XORr(p, 'A');}, - - 0xB0: function(p){ops.ORr(p, 'B');}, - 0xB1: function(p){ops.ORr(p, 'C');}, - 0xB2: function(p){ops.ORr(p, 'D');}, - 0xB3: function(p){ops.ORr(p, 'E');}, - 0xB4: function(p){ops.ORr(p, 'H');}, - 0xB5: function(p){ops.ORr(p, 'L');}, - 0xB6: function(p){ops.ORrra(p, 'H', 'L');}, - 0xB7: function(p){ops.ORr(p, 'A');}, - 0xB8: function(p){ops.CPr(p, 'B');}, - 0xB9: function(p){ops.CPr(p, 'C');}, - 0xBA: function(p){ops.CPr(p, 'D');}, - 0xBB: function(p){ops.CPr(p, 'E');}, - 0xBC: function(p){ops.CPr(p, 'H');}, - 0xBD: function(p){ops.CPr(p, 'L');}, - 0xBE: function(p){ops.CPrra(p, 'H', 'L');}, - 0xBF: function(p){ops.CPr(p, 'A');}, - - 0xC0: function(p){ops.RETcc(p, 'NZ');}, - 0xC1: function(p){ops.POPrr(p, 'B', 'C');}, - 0xC2: function(p){ops.JPccnn(p, 'NZ');}, - 0xC3: function(p){ops.JPnn(p);}, - 0xC4: function(p){ops.CALLccnn(p, 'NZ');}, - 0xC5: function(p){ops.PUSHrr(p, 'B', 'C');}, - 0xC6: function(p){ops.ADDrn(p, 'A');}, - 0xC7: function(p){ops.RSTn(p, 0x00);}, - 0xC8: function(p){ops.RETcc(p, 'Z');}, - 0xC9: function(p){ops.RET(p);}, - 0xCA: function(p){ops.JPccnn(p, 'Z');}, - 0xCB: function(p){ops.CB(p);}, - 0xCC: function(p){ops.CALLccnn(p, 'Z');}, - 0xCD: function(p){ops.CALLnn(p);}, - 0xCE: function(p){ops.ADCrn(p, 'A');}, - 0xCF: function(p){ops.RSTn(p, 0x08);}, - - 0xD0: function(p){ops.RETcc(p, 'NC');}, - 0xD1: function(p){ops.POPrr(p, 'D', 'E');}, - 0xD2: function(p){ops.JPccnn(p, 'NC');}, - //0xD3 empty - 0xD4: function(p){ops.CALLccnn(p, 'NC');}, - 0xD5: function(p){ops.PUSHrr(p, 'D', 'E');}, - 0xD6: function(p){ops.SUBn(p);}, - 0xD7: function(p){ops.RSTn(p, 0x10);}, - 0xD8: function(p){ops.RETcc(p, 'C');}, - 0xD9: function(p){ops.RETI(p);}, - 0xDA: function(p){ops.JPccnn(p, 'C');}, - //0xDB empty - 0xDC: function(p){ops.CALLccnn(p, 'C');}, - //0xDD empty - 0xDE: function(p){ops.SBCn(p);}, - 0xDF: function(p){ops.RSTn(p, 0x18);}, - - 0xE0: function(p){ops.LDHnar(p, 'A');}, - 0xE1: function(p){ops.POPrr(p, 'H', 'L');}, - 0xE2: function(p){ops.LDrar(p, 'C', 'A');}, - //0xE3 empty - //0xE4 empty - 0xE5: function(p){ops.PUSHrr(p, 'H', 'L');}, - 0xE6: function(p){ops.ANDn(p);}, - 0xE7: function(p){ops.RSTn(p, 0x20);}, - 0xE8: function(p){ops.ADDspn(p);}, - 0xE9: function(p){ops.JPrr(p, 'H', 'L');}, - 0xEA: function(p){ops.LDnnar(p, 'A');}, - //0xEB empty - //0xEC empty - //0xED empty - 0xEE: function(p){ops.XORn(p);}, - 0xEF: function(p){ops.RSTn(p, 0x28);}, - - 0xF0: function(p){ops.LDHrna(p, 'A');}, - 0xF1: function(p){ops.POPrr(p, 'A', 'F');}, - 0xF2: function(p){ops.LDrra(p, 'A', 'C');}, - 0xF3: function(p){ops.DI(p);}, - //0xF4 empty - 0xF5: function(p){ops.PUSHrr(p, 'A', 'F');}, - 0xF6: function(p){ops.ORn(p);}, - 0xF7: function(p){ops.RSTn(p, 0x30);}, - 0xF8: function(p){ops.LDrrspn(p, 'H', 'L');}, - 0xF9: function(p){ops.LDsprr(p, 'H', 'L');}, - 0xFA: function(p){ops.LDrnna(p, 'A');}, - 0xFB: function(p){ops.EI(p);}, - //0xFC empty - //0xFD empty - 0xFE: function(p){ops.CPn(p);}, - 0xFF: function(p){ops.RSTn(p, 0x38);} -}; - -var cbmap = { - 0x00: function(p){ops.RLCr(p, 'B');}, - 0x01: function(p){ops.RLCr(p, 'C');}, - 0x02: function(p){ops.RLCr(p, 'D');}, - 0x03: function(p){ops.RLCr(p, 'E');}, - 0x04: function(p){ops.RLCr(p, 'H');}, - 0x05: function(p){ops.RLCr(p, 'L');}, - 0x06: function(p){ops.RLCrra(p, 'H', 'L');}, - 0x07: function(p){ops.RLCr(p, 'A');}, - 0x08: function(p){ops.RRCr(p, 'B');}, - 0x09: function(p){ops.RRCr(p, 'C');}, - 0x0A: function(p){ops.RRCr(p, 'D');}, - 0x0B: function(p){ops.RRCr(p, 'E');}, - 0x0C: function(p){ops.RRCr(p, 'H');}, - 0x0D: function(p){ops.RRCr(p, 'L');}, - 0x0E: function(p){ops.RRCrra(p, 'H', 'L');}, - 0x0F: function(p){ops.RRCr(p, 'A');}, - - 0x10: function(p){ops.RLr(p, 'B');}, - 0x11: function(p){ops.RLr(p, 'C');}, - 0x12: function(p){ops.RLr(p, 'D');}, - 0x13: function(p){ops.RLr(p, 'E');}, - 0x14: function(p){ops.RLr(p, 'H');}, - 0x15: function(p){ops.RLr(p, 'L');}, - 0x16: function(p){ops.RLrra(p, 'H', 'L');}, - 0x17: function(p){ops.RLr(p, 'A');}, - 0x18: function(p){ops.RRr(p, 'B');}, - 0x19: function(p){ops.RRr(p, 'C');}, - 0x1A: function(p){ops.RRr(p, 'D');}, - 0x1B: function(p){ops.RRr(p, 'E');}, - 0x1C: function(p){ops.RRr(p, 'H');}, - 0x1D: function(p){ops.RRr(p, 'L');}, - 0x1E: function(p){ops.RRrra(p, 'H', 'L');}, - 0x1F: function(p){ops.RRr(p, 'A');}, - - 0x20: function(p){ops.SLAr(p, 'B');}, - 0x21: function(p){ops.SLAr(p, 'C');}, - 0x22: function(p){ops.SLAr(p, 'D');}, - 0x23: function(p){ops.SLAr(p, 'E');}, - 0x24: function(p){ops.SLAr(p, 'H');}, - 0x25: function(p){ops.SLAr(p, 'L');}, - 0x26: function(p){ops.SLArra(p, 'H', 'L');}, - 0x27: function(p){ops.SLAr(p, 'A');}, - 0x28: function(p){ops.SRAr(p, 'B');}, - 0x29: function(p){ops.SRAr(p, 'C');}, - 0x2A: function(p){ops.SRAr(p, 'D');}, - 0x2B: function(p){ops.SRAr(p, 'E');}, - 0x2C: function(p){ops.SRAr(p, 'H');}, - 0x2D: function(p){ops.SRAr(p, 'L');}, - 0x2E: function(p){ops.SRArra(p, 'H', 'L');}, - 0x2F: function(p){ops.SRAr(p, 'A');}, - - 0x30: function(p){ops.SWAPr(p, 'B');}, - 0x31: function(p){ops.SWAPr(p, 'C');}, - 0x32: function(p){ops.SWAPr(p, 'D');}, - 0x33: function(p){ops.SWAPr(p, 'E');}, - 0x34: function(p){ops.SWAPr(p, 'H');}, - 0x35: function(p){ops.SWAPr(p, 'L');}, - 0x36: function(p){ops.SWAPrra(p, 'H', 'L');}, - 0x37: function(p){ops.SWAPr(p, 'A');}, - 0x38: function(p){ops.SRLr(p, 'B');}, - 0x39: function(p){ops.SRLr(p, 'C');}, - 0x3A: function(p){ops.SRLr(p, 'D');}, - 0x3B: function(p){ops.SRLr(p, 'E');}, - 0x3C: function(p){ops.SRLr(p, 'H');}, - 0x3D: function(p){ops.SRLr(p, 'L');}, - 0x3E: function(p){ops.SRLrra(p, 'H', 'L');}, - 0x3F: function(p){ops.SRLr(p, 'A');}, - - 0x40: function(p){ops.BITir(p, 0, 'B');}, - 0x41: function(p){ops.BITir(p, 0, 'C');}, - 0x42: function(p){ops.BITir(p, 0, 'D');}, - 0x43: function(p){ops.BITir(p, 0, 'E');}, - 0x44: function(p){ops.BITir(p, 0, 'H');}, - 0x45: function(p){ops.BITir(p, 0, 'L');}, - 0x46: function(p){ops.BITirra(p, 0, 'H', 'L');}, - 0x47: function(p){ops.BITir(p, 0, 'A');}, - 0x48: function(p){ops.BITir(p, 1, 'B');}, - 0x49: function(p){ops.BITir(p, 1, 'C');}, - 0x4A: function(p){ops.BITir(p, 1, 'D');}, - 0x4B: function(p){ops.BITir(p, 1, 'E');}, - 0x4C: function(p){ops.BITir(p, 1, 'H');}, - 0x4D: function(p){ops.BITir(p, 1, 'L');}, - 0x4E: function(p){ops.BITirra(p, 1, 'H', 'L');}, - 0x4F: function(p){ops.BITir(p, 1, 'A');}, - - 0x50: function(p){ops.BITir(p, 2, 'B');}, - 0x51: function(p){ops.BITir(p, 2, 'C');}, - 0x52: function(p){ops.BITir(p, 2, 'D');}, - 0x53: function(p){ops.BITir(p, 2, 'E');}, - 0x54: function(p){ops.BITir(p, 2, 'H');}, - 0x55: function(p){ops.BITir(p, 2, 'L');}, - 0x56: function(p){ops.BITirra(p, 2, 'H', 'L');}, - 0x57: function(p){ops.BITir(p, 2, 'A');}, - 0x58: function(p){ops.BITir(p, 3, 'B');}, - 0x59: function(p){ops.BITir(p, 3, 'C');}, - 0x5A: function(p){ops.BITir(p, 3, 'D');}, - 0x5B: function(p){ops.BITir(p, 3, 'E');}, - 0x5C: function(p){ops.BITir(p, 3, 'H');}, - 0x5D: function(p){ops.BITir(p, 3, 'L');}, - 0x5E: function(p){ops.BITirra(p, 3, 'H', 'L');}, - 0x5F: function(p){ops.BITir(p, 3, 'A');}, - - 0x60: function(p){ops.BITir(p, 4, 'B');}, - 0x61: function(p){ops.BITir(p, 4, 'C');}, - 0x62: function(p){ops.BITir(p, 4, 'D');}, - 0x63: function(p){ops.BITir(p, 4, 'E');}, - 0x64: function(p){ops.BITir(p, 4, 'H');}, - 0x65: function(p){ops.BITir(p, 4, 'L');}, - 0x66: function(p){ops.BITirra(p, 4, 'H', 'L');}, - 0x67: function(p){ops.BITir(p, 4, 'A');}, - 0x68: function(p){ops.BITir(p, 5, 'B');}, - 0x69: function(p){ops.BITir(p, 5, 'C');}, - 0x6A: function(p){ops.BITir(p, 5, 'D');}, - 0x6B: function(p){ops.BITir(p, 5, 'E');}, - 0x6C: function(p){ops.BITir(p, 5, 'H');}, - 0x6D: function(p){ops.BITir(p, 5, 'L');}, - 0x6E: function(p){ops.BITirra(p, 5, 'H', 'L');}, - 0x6F: function(p){ops.BITir(p, 5, 'A');}, - - 0x70: function(p){ops.BITir(p, 6, 'B');}, - 0x71: function(p){ops.BITir(p, 6, 'C');}, - 0x72: function(p){ops.BITir(p, 6, 'D');}, - 0x73: function(p){ops.BITir(p, 6, 'E');}, - 0x74: function(p){ops.BITir(p, 6, 'H');}, - 0x75: function(p){ops.BITir(p, 6, 'L');}, - 0x76: function(p){ops.BITirra(p, 6, 'H', 'L');}, - 0x77: function(p){ops.BITir(p, 6, 'A');}, - 0x78: function(p){ops.BITir(p, 7, 'B');}, - 0x79: function(p){ops.BITir(p, 7, 'C');}, - 0x7A: function(p){ops.BITir(p, 7, 'D');}, - 0x7B: function(p){ops.BITir(p, 7, 'E');}, - 0x7C: function(p){ops.BITir(p, 7, 'H');}, - 0x7D: function(p){ops.BITir(p, 7, 'L');}, - 0x7E: function(p){ops.BITirra(p, 7, 'H', 'L');}, - 0x7F: function(p){ops.BITir(p, 7, 'A');}, - - 0x80: function(p){ops.RESir(p, 0, 'B');}, - 0x81: function(p){ops.RESir(p, 0, 'C');}, - 0x82: function(p){ops.RESir(p, 0, 'D');}, - 0x83: function(p){ops.RESir(p, 0, 'E');}, - 0x84: function(p){ops.RESir(p, 0, 'H');}, - 0x85: function(p){ops.RESir(p, 0, 'L');}, - 0x86: function(p){ops.RESirra(p, 0, 'H', 'L');}, - 0x87: function(p){ops.RESir(p, 0, 'A');}, - 0x88: function(p){ops.RESir(p, 1, 'B');}, - 0x89: function(p){ops.RESir(p, 1, 'C');}, - 0x8A: function(p){ops.RESir(p, 1, 'D');}, - 0x8B: function(p){ops.RESir(p, 1, 'E');}, - 0x8C: function(p){ops.RESir(p, 1, 'H');}, - 0x8D: function(p){ops.RESir(p, 1, 'L');}, - 0x8E: function(p){ops.RESirra(p, 1, 'H', 'L');}, - 0x8F: function(p){ops.RESir(p, 1, 'A');}, - - 0x90: function(p){ops.RESir(p, 2, 'B');}, - 0x91: function(p){ops.RESir(p, 2, 'C');}, - 0x92: function(p){ops.RESir(p, 2, 'D');}, - 0x93: function(p){ops.RESir(p, 2, 'E');}, - 0x94: function(p){ops.RESir(p, 2, 'H');}, - 0x95: function(p){ops.RESir(p, 2, 'L');}, - 0x96: function(p){ops.RESirra(p, 2, 'H', 'L');}, - 0x97: function(p){ops.RESir(p, 2, 'A');}, - 0x98: function(p){ops.RESir(p, 3, 'B');}, - 0x99: function(p){ops.RESir(p, 3, 'C');}, - 0x9A: function(p){ops.RESir(p, 3, 'D');}, - 0x9B: function(p){ops.RESir(p, 3, 'E');}, - 0x9C: function(p){ops.RESir(p, 3, 'H');}, - 0x9D: function(p){ops.RESir(p, 3, 'L');}, - 0x9E: function(p){ops.RESirra(p, 3, 'H', 'L');}, - 0x9F: function(p){ops.RESir(p, 3, 'A');}, - - 0xA0: function(p){ops.RESir(p, 4, 'B');}, - 0xA1: function(p){ops.RESir(p, 4, 'C');}, - 0xA2: function(p){ops.RESir(p, 4, 'D');}, - 0xA3: function(p){ops.RESir(p, 4, 'E');}, - 0xA4: function(p){ops.RESir(p, 4, 'H');}, - 0xA5: function(p){ops.RESir(p, 4, 'L');}, - 0xA6: function(p){ops.RESirra(p, 4, 'H', 'L');}, - 0xA7: function(p){ops.RESir(p, 4, 'A');}, - 0xA8: function(p){ops.RESir(p, 5, 'B');}, - 0xA9: function(p){ops.RESir(p, 5, 'C');}, - 0xAA: function(p){ops.RESir(p, 5, 'D');}, - 0xAB: function(p){ops.RESir(p, 5, 'E');}, - 0xAC: function(p){ops.RESir(p, 5, 'H');}, - 0xAD: function(p){ops.RESir(p, 5, 'L');}, - 0xAE: function(p){ops.RESirra(p, 5, 'H', 'L');}, - 0xAF: function(p){ops.RESir(p, 5, 'A');}, - - 0xB0: function(p){ops.RESir(p, 6, 'B');}, - 0xB1: function(p){ops.RESir(p, 6, 'C');}, - 0xB2: function(p){ops.RESir(p, 6, 'D');}, - 0xB3: function(p){ops.RESir(p, 6, 'E');}, - 0xB4: function(p){ops.RESir(p, 6, 'H');}, - 0xB5: function(p){ops.RESir(p, 6, 'L');}, - 0xB6: function(p){ops.RESirra(p, 6, 'H', 'L');}, - 0xB7: function(p){ops.RESir(p, 6, 'A');}, - 0xB8: function(p){ops.RESir(p, 7, 'B');}, - 0xB9: function(p){ops.RESir(p, 7, 'C');}, - 0xBA: function(p){ops.RESir(p, 7, 'D');}, - 0xBB: function(p){ops.RESir(p, 7, 'E');}, - 0xBC: function(p){ops.RESir(p, 7, 'H');}, - 0xBD: function(p){ops.RESir(p, 7, 'L');}, - 0xBE: function(p){ops.RESirra(p, 7, 'H', 'L');}, - 0xBF: function(p){ops.RESir(p, 7, 'A');}, - - 0xC0: function(p){ops.SETir(p, 0, 'B');}, - 0xC1: function(p){ops.SETir(p, 0, 'C');}, - 0xC2: function(p){ops.SETir(p, 0, 'D');}, - 0xC3: function(p){ops.SETir(p, 0, 'E');}, - 0xC4: function(p){ops.SETir(p, 0, 'H');}, - 0xC5: function(p){ops.SETir(p, 0, 'L');}, - 0xC6: function(p){ops.SETirra(p, 0, 'H', 'L');}, - 0xC7: function(p){ops.SETir(p, 0, 'A');}, - 0xC8: function(p){ops.SETir(p, 1, 'B');}, - 0xC9: function(p){ops.SETir(p, 1, 'C');}, - 0xCA: function(p){ops.SETir(p, 1, 'D');}, - 0xCB: function(p){ops.SETir(p, 1, 'E');}, - 0xCC: function(p){ops.SETir(p, 1, 'H');}, - 0xCD: function(p){ops.SETir(p, 1, 'L');}, - 0xCE: function(p){ops.SETirra(p, 1, 'H', 'L');}, - 0xCF: function(p){ops.SETir(p, 1, 'A');}, - - 0xD0: function(p){ops.SETir(p, 2, 'B');}, - 0xD1: function(p){ops.SETir(p, 2, 'C');}, - 0xD2: function(p){ops.SETir(p, 2, 'D');}, - 0xD3: function(p){ops.SETir(p, 2, 'E');}, - 0xD4: function(p){ops.SETir(p, 2, 'H');}, - 0xD5: function(p){ops.SETir(p, 2, 'L');}, - 0xD6: function(p){ops.SETirra(p, 2, 'H', 'L');}, - 0xD7: function(p){ops.SETir(p, 2, 'A');}, - 0xD8: function(p){ops.SETir(p, 3, 'B');}, - 0xD9: function(p){ops.SETir(p, 3, 'C');}, - 0xDA: function(p){ops.SETir(p, 3, 'D');}, - 0xDB: function(p){ops.SETir(p, 3, 'E');}, - 0xDC: function(p){ops.SETir(p, 3, 'H');}, - 0xDD: function(p){ops.SETir(p, 3, 'L');}, - 0xDE: function(p){ops.SETirra(p, 3, 'H', 'L');}, - 0xDF: function(p){ops.SETir(p, 3, 'A');}, - - 0xE0: function(p){ops.SETir(p, 4, 'B');}, - 0xE1: function(p){ops.SETir(p, 4, 'C');}, - 0xE2: function(p){ops.SETir(p, 4, 'D');}, - 0xE3: function(p){ops.SETir(p, 4, 'E');}, - 0xE4: function(p){ops.SETir(p, 4, 'H');}, - 0xE5: function(p){ops.SETir(p, 4, 'L');}, - 0xE6: function(p){ops.SETirra(p, 4, 'H', 'L');}, - 0xE7: function(p){ops.SETir(p, 4, 'A');}, - 0xE8: function(p){ops.SETir(p, 5, 'B');}, - 0xE9: function(p){ops.SETir(p, 5, 'C');}, - 0xEA: function(p){ops.SETir(p, 5, 'D');}, - 0xEB: function(p){ops.SETir(p, 5, 'E');}, - 0xEC: function(p){ops.SETir(p, 5, 'H');}, - 0xED: function(p){ops.SETir(p, 5, 'L');}, - 0xEE: function(p){ops.SETirra(p, 5, 'H', 'L');}, - 0xEF: function(p){ops.SETir(p, 5, 'A');}, - - 0xF0: function(p){ops.SETir(p, 6, 'B');}, - 0xF1: function(p){ops.SETir(p, 6, 'C');}, - 0xF2: function(p){ops.SETir(p, 6, 'D');}, - 0xF3: function(p){ops.SETir(p, 6, 'E');}, - 0xF4: function(p){ops.SETir(p, 6, 'H');}, - 0xF5: function(p){ops.SETir(p, 6, 'L');}, - 0xF6: function(p){ops.SETirra(p, 6, 'H', 'L');}, - 0xF7: function(p){ops.SETir(p, 6, 'A');}, - 0xF8: function(p){ops.SETir(p, 7, 'B');}, - 0xF9: function(p){ops.SETir(p, 7, 'C');}, - 0xFA: function(p){ops.SETir(p, 7, 'D');}, - 0xFB: function(p){ops.SETir(p, 7, 'E');}, - 0xFC: function(p){ops.SETir(p, 7, 'H');}, - 0xFD: function(p){ops.SETir(p, 7, 'L');}, - 0xFE: function(p){ops.SETirra(p, 7, 'H', 'L');}, - 0xFF: function(p){ops.SETir(p, 7, 'A');} -}; -GameboyJS.opcodeMap = map; -GameboyJS.opcodeCbmap = cbmap; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// A RomAjaxReader is able to load a file through an AJAX request -var RomAjaxReader = function() { - -}; - -// The callback argument will be called when a file is successfully -// read, with the data as argument (Uint8Array) -RomAjaxReader.prototype.setCallback = function(onLoadCallback) { - this.callback = onLoadCallback; -}; - -// This function should be called by application code -// and will trigger the AJAX call itself and push data to the ROM object -RomAjaxReader.prototype.loadFromUrl = function(url) { - if (!url) { - throw 'No url has been set in order to load a ROM file.'; - } - var cb = this.callback; - - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = "arraybuffer"; - xhr.onload = function() { - var rom = new Uint8Array(xhr.response); - cb && cb(rom); - }; - - xhr.send(); -}; - -GameboyJS.RomAjaxReader = RomAjaxReader; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// A RomDropFileReader is able to load a drag and dropped file -var RomDropFileReader = function(el) { - this.dropElement = el; - if (!this.dropElement) { - throw 'The RomDropFileReader needs a drop zone.'; - } - - var self = this; - this.dropElement.addEventListener('dragenter', function(e) { - e.preventDefault(); - e.target.classList.add('drag-active'); - }); - this.dropElement.addEventListener('dragleave', function(e) { - e.preventDefault(); - e.target.classList.remove('drag-active'); - }); - this.dropElement.addEventListener('dragover', function(e) { - e.preventDefault(); - }); - this.dropElement.addEventListener('drop', function (e) { - e.target.classList.remove('drag-active'); - if (e.dataTransfer.files.length == 0) { - return; - } - e.preventDefault(); - self.loadFromFile(e.dataTransfer.files[0]); - }); -}; - -// The callback argument will be called when a file is successfully -// read, with the data as argument (Uint8Array) -RomDropFileReader.prototype.setCallback = function(onLoadCallback) { - this.callback = onLoadCallback; -}; - -// The file loading logic is the same as the regular file reader -RomDropFileReader.prototype.loadFromFile = function(file) { - if (file === undefined) { - return; - } - var fr = new FileReader(); - var cb = this.callback; - - fr.onload = function() { - cb && cb(new Uint8Array(fr.result)); - }; - fr.onerror = function(e) { - console.log('Error reading the file', e.target.error.code) - }; - fr.readAsArrayBuffer(file); -}; - -GameboyJS.RomDropFileReader = RomDropFileReader; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// A RomFileReader is able to load a local file from an input element -// -// Expects to be provided a file input element, -// or will try to find one with the "file" DOM ID -var RomFileReader = function(el) { - this.domElement = el || document.getElementById('file'); - if (!this.domElement) { - throw 'The RomFileReader needs a valid input element.'; - } - - var self = this; - this.domElement.addEventListener('change', function(e){ - self.loadFromFile(e.target.files[0]); - }); -}; - -// The callback argument will be called when a file is successfully -// read, with the data as argument (Uint8Array) -RomFileReader.prototype.setCallback = function(onLoadCallback) { - this.callback = onLoadCallback; -}; - -// Automatically called when the DOM input is provided with a file -RomFileReader.prototype.loadFromFile = function(file) { - if (file === undefined) { - return; - } - var fr = new FileReader(); - var cb = this.callback; - - fr.onload = function() { - cb && cb(new Uint8Array(fr.result)); - }; - fr.onerror = function(e) { - console.log('Error reading the file', e.target.error.code) - }; - fr.readAsArrayBuffer(file); -}; - -GameboyJS.RomFileReader = RomFileReader; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - - -var Rom = function(gameboy, romReader) { - this.gameboy = gameboy; - if (romReader) { - this.addReader(romReader); - } -}; - -Rom.prototype.addReader = function(romReader) { - var self = this; - romReader.setCallback(function(data) { - if (!validate(data)) { - self.gameboy.error('The file is not a valid GameBoy ROM.'); - return; - } - self.data = data; - self.gameboy.startRom(self); - }); -}; - -// Validate the checksum of the cartridge header -function validate(data) { - var hash = 0; - for (var i = 0x134; i <= 0x14C; i++) { - hash = hash - data[i] - 1; - } - return (hash & 0xFF) == data[0x14D]; -}; - -GameboyJS.Rom = Rom; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Handlers for the Serial port of the Gameboy - -// The ConsoleSerial is an output-only serial port -// designed for debug purposes as some test roms output data on the serial port -// -// Will regularly output the received byte (converted to string) in the console logs -// This handler always push the value 0xFF as an input -var ConsoleSerial = { - current: '', - timeout: null, - out: function(data) { - ConsoleSerial.current += String.fromCharCode(data); - if (data == 10) { - ConsoleSerial.print(); - } else { - clearTimeout(ConsoleSerial.timeout); - ConsoleSerial.timeout = setTimeout(ConsoleSerial.print, 500); - } - }, - in: function() { - return 0xFF; - }, - print: function() { - clearTimeout(ConsoleSerial.timeout); - console.log('serial: '+ConsoleSerial.current); - ConsoleSerial.current = ''; - } -}; -GameboyJS.ConsoleSerial = ConsoleSerial; - -// A DummySerial outputs nothing and always inputs 0xFF -var DummySerial = { - out: function() {}, - in: function() { - return 0xFF; - } -}; -GameboyJS.DummySerial = DummySerial; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Audio Processing unit -// Listens the write accesses to the audio-reserved memory addresses -// and dispatches the data to the sound channels -var APU = function(memory) { - this.memory = memory; - this.enabled = false; - - AudioContext = window.AudioContext || window.webkitAudioContext; - var audioContext = new AudioContext(); - - this.channel1 = new GameboyJS.Channel1(this, 1, audioContext); - this.channel2 = new GameboyJS.Channel1(this, 2, audioContext); - this.channel3 = new GameboyJS.Channel3(this, 3, audioContext); - this.channel4 = new GameboyJS.Channel4(this, 4, audioContext); - -}; -APU.prototype.connect = function() { - this.channel1.enable(); - this.channel2.enable(); - this.channel3.enable(); -}; -APU.prototype.disconnect = function() { - this.channel1.disable(); - this.channel2.disable(); - this.channel3.disable(); -}; -// Updates the states of each channel given the elapsed time -// (in instructions) since last update -APU.prototype.update = function(clockElapsed) { - if (this.enabled == false) return; - - this.channel1.update(clockElapsed); - this.channel2.update(clockElapsed); - this.channel3.update(clockElapsed); - this.channel4.update(clockElapsed); -}; -APU.prototype.setSoundFlag = function(channel, value) { - var mask = 0xFF - (1 << (channel - 1)); - value = value << (channel - 1) - var byteValue = this.memory.rb(APU.registers.NR52); - byteValue &= mask; - byteValue |= value; - this.memory[APU.registers.NR52] = byteValue; -}; -// Manage writes to audio registers -// Will update the channels depending on the address -APU.prototype.manageWrite = function(addr, value) { - if (this.enabled == false && addr < APU.registers.NR52) { - return; - } - this.memory[addr] = value; - - switch (addr) { - // Channel 1 addresses - case 0xFF10: - this.channel1.clockSweep = 0; - this.channel1.sweepTime = ((value & 0x70) >> 4); - this.channel1.sweepSign = (value & 0x08) ? -1 : 1; - this.channel1.sweepShifts = (value & 0x07); - this.channel1.sweepCount = this.channel1.sweepShifts; - break; - case 0xFF11: - // todo : bits 6-7 - this.channel1.setLength(value & 0x3F); - break; - case 0xFF12: - this.channel1.envelopeSign = (value & 0x08) ? 1 : -1; - var envelopeVolume = (value & 0xF0) >> 4; - this.channel1.setEnvelopeVolume(envelopeVolume); - this.channel1.envelopeStep = (value & 0x07); - break; - case 0xFF13: - var frequency = this.channel1.getFrequency(); - frequency &= 0xF00; - frequency |= value; - this.channel1.setFrequency(frequency); - break; - case 0xFF14: - var frequency = this.channel1.getFrequency(); - frequency &= 0xFF; - frequency |= (value & 7) << 8; - this.channel1.setFrequency(frequency); - this.channel1.lengthCheck = (value & 0x40) ? true : false; - if (value & 0x80) this.channel1.play(); - break; - - // Channel 2 addresses - case 0xFF16: - // todo : bits 6-7 - this.channel2.setLength(value & 0x3F); - break; - case 0xFF17: - this.channel2.envelopeSign = (value & 0x08) ? 1 : -1; - var envelopeVolume = (value & 0xF0) >> 4; - this.channel2.setEnvelopeVolume(envelopeVolume); - this.channel2.envelopeStep = (value & 0x07); - break; - case 0xFF18: - var frequency = this.channel2.getFrequency(); - frequency &= 0xF00; - frequency |= value; - this.channel2.setFrequency(frequency); - break; - case 0xFF19: - var frequency = this.channel2.getFrequency(); - frequency &= 0xFF; - frequency |= (value & 7) << 8; - this.channel2.setFrequency(frequency); - this.channel2.lengthCheck = (value & 0x40) ? true : false; - if (value & 0x80) { - this.channel2.play(); - } - break; - - // Channel 3 addresses - case 0xFF1A: - // todo - break; - case 0xFF1B: - this.channel3.setLength(value); - break; - case 0xFF1C: - // todo - break; - case 0xFF1D: - var frequency = this.channel3.getFrequency(); - frequency &= 0xF00; - frequency |= value; - this.channel3.setFrequency(frequency); - break; - case 0xFF1E: - var frequency = this.channel3.getFrequency(); - frequency &= 0xFF; - frequency |= (value & 7) << 8; - this.channel3.setFrequency(frequency); - this.channel3.lengthCheck = (value & 0x40) ? true : false; - if (value & 0x80) { - this.channel3.play(); - } - break; - - // Channel 4 addresses - case 0xFF20: - this.channel4.setLength(value & 0x3F); - break; - case 0xFF21: - // todo - break; - case 0xFF22: - // todo - break; - case 0xFF23: - this.channel4.lengthCheck = (value & 0x40) ? true : false; - if (value & 0x80) { - this.channel4.play(); - } - break; - - // channel 3 wave bytes - case 0xFF30:case 0xFF31:case 0xFF32:case 0xFF33:case 0xFF34:case 0xFF35:case 0xFF36:case 0xFF37: - case 0xFF38:case 0xFF39:case 0xFF3A:case 0xFF3B:case 0xFF3C:case 0xFF3D:case 0xFF3E:case 0xFF3F: - var index = addr - 0xFF30; - this.channel3.setWaveBufferByte(index, value); - break; - - // general audio switch - case 0xFF26: - value &= 0xF0; - this.memory[addr] = value; - this.enabled = (value & 0x80) == 0 ? false : true; - if (!this.enabled) { - for (var i = 0xFF10; i < 0xFF27; i++) - this.memory[i] = 0; - // todo stop sound - } - break; - } -}; - -APU.registers = { - NR10: 0xFF10, - NR11: 0xFF11, - NR12: 0xFF12, - NR13: 0xFF13, - NR14: 0xFF14, - - NR21: 0xFF16, - NR22: 0xFF17, - NR23: 0xFF18, - NR24: 0xFF19, - - NR30: 0xFF1A, - NR31: 0xFF1B, - NR32: 0xFF1C, - NR33: 0xFF1D, - NR34: 0xFF1E, - - NR41: 0xFF20, - NR42: 0xFF21, - NR43: 0xFF22, - NR44: 0xFF23, - - NR50: 0xFF24, - NR51: 0xFF25, - NR52: 0xFF26 -}; -GameboyJS.APU = APU; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; -var Channel1 = function(apu, channelNumber, audioContext) { - this.apu = apu; - this.channelNumber = channelNumber; - this.playing = false; - - this.soundLengthUnit = 0x4000; // 1 / 256 second of instructions - this.soundLength = 64; // defaults to 64 periods - this.lengthCheck = false; - - this.sweepTime = 0; // from 0 to 7 - this.sweepStepLength = 0x8000; // 1 / 128 seconds of instructions - this.sweepCount = 0; - this.sweepShifts = 0; - this.sweepSign = 1; // +1 / -1 for increase / decrease freq - - this.frequency = 0; - - this.envelopeStep = 0; - this.envelopeStepLength = 0x10000;// 1 / 64 seconds of instructions - this.envelopeCheck = false; - this.envelopeSign = 1; - - this.clockLength = 0; - this.clockEnvelop = 0; - this.clockSweep = 0; - - var gainNode = audioContext.createGain(); - gainNode.gain.value = 0; - var oscillator = audioContext.createOscillator(); - oscillator.type = 'square'; - oscillator.frequency.value = 1000; - oscillator.connect(gainNode); - oscillator.start(0); - - this.audioContext = audioContext; - this.gainNode = gainNode; - this.oscillator = oscillator; -}; - -Channel1.prototype.play = function() { - if (this.playing) return; - this.playing = true; - this.apu.setSoundFlag(this.channelNumber, 1); - this.gainNode.connect(this.audioContext.destination); - this.clockLength = 0; - this.clockEnvelop = 0; - this.clockSweep = 0; - if (this.sweepShifts > 0) this.checkFreqSweep(); -}; -Channel1.prototype.stop = function() { - this.playing = false; - this.apu.setSoundFlag(this.channelNumber, 0); - this.gainNode.disconnect(); -}; -Channel1.prototype.checkFreqSweep = function() { - var oldFreq = this.getFrequency(); - var newFreq = oldFreq + this.sweepSign * (oldFreq >> this.sweepShifts); - if (newFreq > 0x7FF) { - newFreq = 0; - this.stop(); - } - - return newFreq; -}; -Channel1.prototype.update = function(clockElapsed) { - this.clockEnvelop += clockElapsed; - this.clockSweep += clockElapsed; - - if ((this.sweepCount || this.sweepTime) && this.clockSweep > (this.sweepStepLength * this.sweepTime)) { - this.clockSweep -= (this.sweepStepLength * this.sweepTime); - this.sweepCount--; - - var newFreq = this.checkFreqSweep(); // process and check new freq - - this.apu.memory[0xFF13] = newFreq & 0xFF; - this.apu.memory[0xFF14] &= 0xF8; - this.apu.memory[0xFF14] |= (newFreq & 0x700) >> 8; - this.setFrequency(newFreq); - - this.checkFreqSweep(); // check again with new value - } - - if (this.envelopeCheck && this.clockEnvelop > this.envelopeStepLength) { - this.clockEnvelop -= this.envelopeStepLength; - this.envelopeStep--; - this.setEnvelopeVolume(this.envelopeVolume + this.envelopeSign); - if (this.envelopeStep <= 0) { - this.envelopeCheck = false; - } - } - - if (this.lengthCheck) { - this.clockLength += clockElapsed; - if (this.clockLength > this.soundLengthUnit) { - this.soundLength--; - this.clockLength -= this.soundLengthUnit; - if (this.soundLength == 0) { - this.setLength(0); - this.stop(); - } - } - } -}; -Channel1.prototype.setFrequency = function(value) { - this.frequency = value; - this.oscillator.frequency.value = 131072 / (2048 - this.frequency); -}; -Channel1.prototype.getFrequency = function() { - return this.frequency; -}; -Channel1.prototype.setLength = function(value) { - this.soundLength = 64 - (value & 0x3F); -}; -Channel1.prototype.setEnvelopeVolume = function(volume) { - this.envelopeCheck = volume > 0 && volume < 16 ? true : false; - this.envelopeVolume = volume; - this.gainNode.gain.value = this.envelopeVolume * 1/100; -}; -Channel1.prototype.disable = function() { - this.oscillator.disconnect(); -}; -Channel1.prototype.enable = function() { - this.oscillator.connect(this.gainNode); -}; -GameboyJS.Channel1 = Channel1; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; -var Channel3 = function(apu, channelNumber, audioContext) { - this.apu = apu; - this.channelNumber = channelNumber; - this.playing = false; - - this.soundLength = 0; - this.soundLengthUnit = 0x4000; // 1 / 256 second of instructions - this.lengthCheck = false; - - this.clockLength = 0; - - this.buffer = new Float32Array(32); - - var gainNode = audioContext.createGain(); - gainNode.gain.value = 1; - this.gainNode = gainNode; - - this.baseSpeed = 65536; - var waveBuffer = audioContext.createBuffer(1, 32, this.baseSpeed); - - var bufferSource = audioContext.createBufferSource(); - bufferSource.buffer = waveBuffer; - bufferSource.loop = true; - bufferSource.connect(gainNode); - bufferSource.start(0); - - this.audioContext = audioContext; - this.waveBuffer = waveBuffer; - this.bufferSource = bufferSource; - -}; -Channel3.prototype.play = function() { - if (this.playing) return; - this.playing = true; - this.apu.setSoundFlag(this.channelNumber, 1); - this.waveBuffer.copyToChannel(this.buffer, 0, 0); - - this.gainNode.connect(this.audioContext.destination); - this.clockLength = 0; -}; -Channel3.prototype.stop = function() { - this.playing = false; - this.apu.setSoundFlag(this.channelNumber, 0); - this.gainNode.disconnect(); -}; -Channel3.prototype.update = function(clockElapsed) { - if (this.lengthCheck){ - this.clockLength += clockElapsed; - if (this.clockLength > this.soundLengthUnit) { - this.soundLength--; - this.clockLength -= this.soundLengthUnit; - if (this.soundLength == 0) { - this.setLength(0); - this.stop(); - } - } - } -}; -Channel3.prototype.setFrequency = function(value) { - value = 65536 / (2048 - value); - this.bufferSource.playbackRate.value = value / this.baseSpeed; -}; -Channel3.prototype.getFrequency = function() { - var freq = 2048 - 65536 / (this.bufferSource.playbackRate.value * this.baseSpeed); - return freq | 1; -}; -Channel3.prototype.setLength = function(value) { - this.soundLength = 256 - value; -}; -Channel3.prototype.setWaveBufferByte = function(index, value) { - var bufferIndex = index * 2; - - this.buffer[bufferIndex] = (value >> 4) / 8 - 1; // value in buffer is in -1 -> 1 - this.buffer[bufferIndex+1] = (value & 0x0F) / 8 - 1; -}; -Channel3.prototype.disable = function() { - this.bufferSource.disconnect(); -}; -Channel3.prototype.enable = function() { - this.bufferSource.connect(this.gainNode); -}; -GameboyJS.Channel3 = Channel3; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; -var Channel4 = function(apu, channelNumber, audioContext) { - this.apu = apu; - this.channelNumber = channelNumber; - this.playing = false; - - this.soundLengthUnit = 0x4000; // 1 / 256 second of instructions - this.soundLength = 64; // defaults to 64 periods - this.lengthCheck = false; - - this.clockLength = 0; - - this.audioContext = audioContext; -}; - -Channel4.prototype.play = function() { - if (this.playing) return; - this.playing = true; - this.apu.setSoundFlag(this.channelNumber, 1); - this.clockLength = 0; -}; -Channel4.prototype.stop = function() { - this.playing = false; - this.apu.setSoundFlag(this.channelNumber, 0); -}; -Channel4.prototype.update = function(clockElapsed) { - if (this.lengthCheck) { - this.clockLength += clockElapsed; - if (this.clockLength > this.soundLengthUnit) { - this.soundLength--; - this.clockLength -= this.soundLengthUnit; - if (this.soundLength == 0) { - this.setLength(0); - this.stop(); - } - } - } -}; -Channel4.prototype.setLength = function(value) { - this.soundLength = 64 - (value & 0x3F); -}; -GameboyJS.Channel4 = Channel4; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -var Timer = function(cpu, memory) { - this.cpu = cpu; - this.memory = memory; - - this.DIV = 0xFF04; - this.TIMA = 0xFF05; - this.TMA = 0xFF06; - this.TAC = 0xFF07; - - this.mainTime = 0; - this.divTime = 0; -}; - -Timer.prototype.update = function(clockElapsed) { - this.updateDiv(clockElapsed); - this.updateTimer(clockElapsed); -}; - -Timer.prototype.updateTimer = function(clockElapsed) { - if (!(this.memory.rb(this.TAC) & 0x4)) { - return; - } - this.mainTime += clockElapsed; - - var threshold = 64; - switch (this.memory.rb(this.TAC) & 3) { - case 0: threshold=64; break; // 4KHz - case 1: threshold=1; break; // 256KHz - case 2: threshold=4; break; // 64KHz - case 3: threshold=16; break; // 16KHz - } - threshold *= 16; - - while (this.mainTime >= threshold) { - this.mainTime -= threshold; - - this.memory.wb(this.TIMA, this.memory.rb(this.TIMA) + 1); - if (this.memory.rb(this.TIMA) > 0xFF) { - this.memory.wb(this.TIMA, this.memory.rb(this.TMA)); - this.cpu.requestInterrupt(GameboyJS.CPU.INTERRUPTS.TIMER); - } - } -}; -// Update the DIV register internal clock -// Increment it if the clock threshold is elapsed and -// reset it if its value overflows -Timer.prototype.updateDiv = function(clockElapsed) { - var divThreshold = 256; // DIV is 16KHz - this.divTime += clockElapsed; - if (this.divTime > divThreshold) { - this.divTime -= divThreshold; - var div = this.memory.rb(this.DIV) + 1; - this.memory.wb(this.DIV, div&0xFF); - } -}; - -Timer.prototype.resetDiv = function() { - this.divTime = 0; - this.memory[this.DIV] = 0; // direct write to avoid looping -}; -GameboyJS.Timer = Timer; -}(GameboyJS || (GameboyJS = {}))); - -var GameboyJS; -(function (GameboyJS) { -"use strict"; - -// Utility functions -var Util = { - // Add to the first argument the properties of all other arguments - extend: function(target /*, source1, source2, etc. */) { - var sources = Array.prototype.slice.call(arguments); - for (var i in sources) { - var source = sources[i]; - for (var name in source) { - target[name] = source[name]; - } - } - - return target; - }, - testFlag: function(p, cc) { - var test=1; - var mask=0x10; - if (cc=='NZ'||cc=='NC') test=0; - if (cc=='NZ'||cc=='Z') mask=0x80; - return (test && p.r.F&mask) || (!test && !(p.r.F&mask)); - }, - getRegAddr: function(p, r1, r2) {return Util.makeword(p.r[r1], p.r[r2]);}, - - // make a 16 bits word from 2 bytes - makeword: function(b1, b2) {return (b1 << 8) + b2;}, - - // return the integer signed value of a given byte - getSignedValue: function(v) {return v & 0x80 ? v-256 : v;}, - - // extract a bit from a byte - readBit: function(byte, index) { - return (byte >> index) & 1; - } -}; - -GameboyJS.Util = Util; -}(GameboyJS || (GameboyJS = {}))); diff --git a/package-lock.json b/package-lock.json index 9344aba1..f9097612 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "alloy-proxy", - "version": "2.3.0", + "name": "holyub", + "version": "4.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,81 +13,11 @@ "negotiator": "0.6.2" } }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, - "babel-polyfill": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", - "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=", - "requires": { - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - } - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - } - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -105,96 +35,15 @@ "type-is": "~1.6.17" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" - } - }, - "chance": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/chance/-/chance-1.0.11.tgz", - "integrity": "sha512-PK8Zmz6kJAkwwJuiTZvjcFX11/H+BvVd2r8dnHrDRXmOLoUHh4Hxiv+YyYTcT7sdFzP9LhMS3O3C/7f+7gSQ2g==" - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" - }, - "class-validator": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.7.2.tgz", - "integrity": "sha1-Ne2G8FkLu2AWGoyO+cgTyzhXL/4=", - "requires": { - "validator": "^7.0.0" - } - }, - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "requires": { - "source-map": "~0.6.0" - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "content-disposition": { "version": "0.5.3", @@ -228,23 +77,10 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "cookies": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.4.1.tgz", - "integrity": "sha1-fUO9AFg8mFrMAyJYuXmIt9A7Yp4=", - "requires": { - "keygrip": "~1.0.0" - } - }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + "cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" }, "debug": { "version": "2.6.9", @@ -254,11 +90,6 @@ "ms": "2.0.0" } }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -269,11 +100,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -284,192 +110,11 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen-wallaby": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/escodegen-wallaby/-/escodegen-wallaby-1.6.12.tgz", - "integrity": "sha512-locntbd0gcCJ1X9kVzFluU4APZ2L9juqbIyUUZltb8T/KU168kN0xPVGVu65h/FxrVZobG1pT1tQAAY6zGy+3A==", - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" - }, - "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=" - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "escope": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escope/-/escope-1.0.3.tgz", - "integrity": "sha1-dZ3OhJbEJI/sLQyq9BCLzz8af10=", - "requires": { - "estraverse": "^2.0.0" - }, - "dependencies": { - "estraverse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-2.0.0.tgz", - "integrity": "sha1-WuRpYyQ2ACBmdMyySgnhZnT83KE=" - } - } - }, - "esmangle": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esmangle/-/esmangle-1.0.1.tgz", - "integrity": "sha1-2bs3uPjq+/Tm1O1reqKVarvTxMI=", - "requires": { - "escodegen": "~1.3.2", - "escope": "~1.0.1", - "esprima": "~1.1.1", - "esshorten": "~1.1.0", - "estraverse": "~1.5.0", - "esutils": "~ 1.0.0", - "optionator": "~0.3.0", - "source-map": "~0.1.33" - }, - "dependencies": { - "escodegen": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", - "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=", - "requires": { - "esprima": "~1.1.1", - "estraverse": "~1.5.0", - "esutils": "~1.0.0", - "source-map": "~0.1.33" - } - }, - "esprima": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", - "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk=" - }, - "estraverse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", - "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" - }, - "esutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", - "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" - }, - "fast-levenshtein": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz", - "integrity": "sha1-AXjc3uAjuSkFGTrwlZ6KdjnP3Lk=" - }, - "levn": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz", - "integrity": "sha1-uo0znQykphDjo/FFucr0iAcVUFQ=", - "requires": { - "prelude-ls": "~1.1.0", - "type-check": "~0.3.1" - } - }, - "optionator": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.3.0.tgz", - "integrity": "sha1-lxWotfXnWGz/BsgkngOc1zZNP1Q=", - "requires": { - "deep-is": "~0.1.2", - "fast-levenshtein": "~1.0.0", - "levn": "~0.2.4", - "prelude-ls": "~1.1.0", - "type-check": "~0.3.1", - "wordwrap": "~0.0.2" - } - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" - }, - "esshorten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/esshorten/-/esshorten-1.1.1.tgz", - "integrity": "sha1-F0+Wt8wmfkaHLYFOfbfCkL3/Yak=", - "requires": { - "escope": "~1.0.1", - "estraverse": "~4.1.1", - "esutils": "~2.0.2" - }, - "dependencies": { - "estraverse": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", - "integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI=" - } - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -539,34 +184,6 @@ } } }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -581,24 +198,6 @@ "unpipe": "~1.0.0" } }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -609,37 +208,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -660,141 +228,16 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "inquirer": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz", - "integrity": "sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c=", - "requires": { - "ansi-escapes": "^1.1.0", - "chalk": "^1.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.1", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx": "^4.1.0", - "string-width": "^2.0.0", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "inversify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/inversify/-/inversify-4.3.0.tgz", - "integrity": "sha512-BbKzOP4nLl7DSHx31vN5KgUkkJc759+OLocDGryldXPjogixzOs/n4qvT25lbNN67uB6aWlFF84NS/zI5j8pfg==" - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "javascript-obfuscator": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/javascript-obfuscator/-/javascript-obfuscator-0.11.2.tgz", - "integrity": "sha512-VMO/CzouHQzXlQ7h2rbDILO6x9fxag9kwxlgUSgNQc9+pjjy6gEAoZuKsrsZRHssgsXVDUDywdnmx+YYQhkiwA==", - "requires": { - "chalk": "2.1.0", - "chance": "1.0.11", - "class-validator": "0.7.2", - "commander": "2.11.0", - "escodegen-wallaby": "1.6.12", - "esmangle": "1.0.1", - "esprima": "4.0.0", - "estraverse": "4.2.0", - "inversify": "4.3.0", - "md5": "2.2.1", - "mkdirp": "0.5.1", - "opencollective": "1.0.3", - "reflect-metadata": "0.1.10", - "source-map-support": "0.4.18", - "string-template": "1.0.0", - "tslib": "1.7.1" - } - }, - "keygrip": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz", - "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -811,9 +254,9 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { "version": "1.44.0", @@ -828,76 +271,16 @@ "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -911,133 +294,16 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "opencollective": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz", - "integrity": "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE=", - "requires": { - "babel-polyfill": "6.23.0", - "chalk": "1.1.3", - "inquirer": "3.0.6", - "minimist": "1.2.0", - "node-fetch": "1.6.3", - "opn": "4.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "node-fetch": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", - "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "opn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", - "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", - "requires": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1073,35 +339,6 @@ "unpipe": "1.0.0" } }, - "reflect-metadata": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", - "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=" - }, - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1112,11 +349,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sanitizer": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/sanitizer/-/sanitizer-0.1.3.tgz", - "integrity": "sha1-1PCvdHXZp7ryqeWmEXGLqheKOeE=" - }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -1137,11 +369,6 @@ "statuses": "~1.5.0" }, "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -1160,125 +387,21 @@ "send": "0.17.1" } }, - "session": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/session/-/session-0.1.0.tgz", - "integrity": "sha1-aumqbyYJO+Me0gBbzUJq6AqjoIU=", - "requires": { - "vows": "*" - } - }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "requires": { - "source-map": "^0.5.6" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "string-template": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz", - "integrity": "sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "^2.0.0" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "tslib": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz", - "integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1306,45 +429,19 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, - "validator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-7.2.0.tgz", - "integrity": "sha512-c8NGTUYeBEcUIGeMppmNVKHE7wwfm3mYbNZxV+c5mlv9fDHI7Ad3p07qfNrn/CvpdkK2k61fOLRO2sTEhgQXmg==" - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "vows": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/vows/-/vows-0.8.3.tgz", - "integrity": "sha512-PVIxa/ovXhrw5gA3mz6M+ZF3PHlqX4tutR2p/y9NWPAaFVKcWBE8b2ktfr0opQM/qFmcOVWKjSCJVjnYOvjXhw==", + "xss": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.8.tgz", + "integrity": "sha512-3MgPdaXV8rfQ/pNn16Eio6VXYPTkqwa0vc7GkiymmY/DqR1SE/7VPAAVZz1GJsJFrllMYO3RHfEaiUGjab6TNw==", "requires": { - "diff": "^4.0.1", - "eyes": "~0.1.6", - "glob": "^7.1.2" + "commander": "^2.20.3", + "cssfilter": "0.0.10" } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" } } } diff --git a/package.json b/package.json index fe2fbd93..ce07859d 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { - "name": "alloy-proxy", - "version": "2.3.0", - "description": "A web proxy capable of proxying websites!", + "name": "holyub", + "version": "4.0.0", + "description": "A pretty fancy website.", "main": "app.js", "scripts": { "test": "node app.js", - "start": "node app.js ./node_modules/.bin/grunt" + "start": "node app.js" }, "keywords": [ "proxy", @@ -13,20 +13,12 @@ "unblocker" ], "author": "Titanium Network", - "license": "ISC", + "license": "MIT", "dependencies": { "cookie-parser": "^1.4.5", "express": "^4.17.1", "express-session": "^1.17.1", - "node-fetch": "^2.6.1", - "sanitizer": "^0.1.3", - "session": "^0.1.0", - "cookies": "0.4", - "ws": "^7.3.1", - "javascript-obfuscator": "^0.11.2", - "clean-css": "^4.1.9", - "mime": "^2.2.0", - "morgan": "^1.9.0", - "axios": "^0.19.2" + "mime-types": "^2.1.27", + "xss": "^1.0.8" } } diff --git a/public/expr/ch.js b/public/expr/ch.js new file mode 100644 index 00000000..35250d1b --- /dev/null +++ b/public/expr/ch.js @@ -0,0 +1,8 @@ +window.onload = function() { + var frame = document.getElementById("frame"); + var det = document.domain; + var domain = det.replace('www.', '').split(/[/?#]/)[0]; + frame.src = "https://c." + domain + "/app"; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + return false; +}; \ No newline at end of file diff --git a/public/expr/surf.js b/public/expr/surf.js index dc28ae92..52957859 100644 --- a/public/expr/surf.js +++ b/public/expr/surf.js @@ -7,9 +7,9 @@ $('al').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(url) - frame.src = "https://" + domain + "/fetch/utils/?url=" + origin; + frame.src = "https://cdn." + domain + "/fetch/utils/?url=" + origin; frame.style['visibility'] = "visible"; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; $('albp').onclick = function() { @@ -18,8 +18,8 @@ $('albp').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(url) - window.location.href = "https://" + domain + "/fetch/utils/?url=" + origin; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + window.location.href = "https://cdn." + domain + "/fetch/utils/?url=" + origin; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; //CH @@ -29,7 +29,7 @@ $('ch').onclick = function() { var domain = det.replace('www.', '').split(/[/?#]/)[0]; frame.src = "https://c." + domain + "/app"; frame.style['visibility'] = "visible"; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; //NU @@ -61,7 +61,7 @@ $('pdprox').onclick = function() { var domain = det.replace('www.', '').split(/[/?#]/)[0]; frame.src = "https://cdn." + domain + "/" + url; frame.style['visibility'] = "visible"; - document.cookie = 'wowow; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; $('pdproxbp').onclick = function() { @@ -70,7 +70,7 @@ $('pdproxbp').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; window.location.href = "https://cdn." + domain + "/" + url; - document.cookie = 'wowo; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; //PM Load @@ -102,9 +102,9 @@ $('ytbtn').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(yt) - frame.src = "https://" + domain + "/fetch/utils/?url=" + origin; + frame.src = "https://cdn." + domain + "/fetch/utils/?url=" + origin; frame.style['visibility'] = "visible"; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; $('ytbtnm').onclick = function() { @@ -113,9 +113,9 @@ $('ytbtnm').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(yt) - frame.src = "https://" + domain + "/fetch/utils/?url=" + origin; + frame.src = "https://cdn." + domain + "/fetch/utils/?url=" + origin; frame.style['visibility'] = "visible"; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; $('ytbp').onclick = function() { @@ -124,8 +124,17 @@ $('ytbp').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(yt) - window.location.href = "https://" + domain + "/fetch/utils/?url=" + origin; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + window.location.href = "https://cdn." + domain + "/fetch/utils/?url=" + origin; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + return false; +}; +$('chat').onclick = function() { + var frame = document.getElementById("frame"); + var det = document.domain; + var domain = det.replace('www.', '').split(/[/?#]/)[0]; + frame.src = "https://c." + domain + "/app"; + frame.style['visibility'] = "visible"; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; //D @@ -135,9 +144,9 @@ $('dbtn').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(d) - frame.src = "https://" + domain + "/fetch/utils/?url=" + origin; + frame.src = "https://cdn." + domain + "/fetch/utils/?url=" + origin; frame.style['visibility'] = "visible"; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; $('dbp').onclick = function() { @@ -146,8 +155,8 @@ $('dbp').onclick = function() { var det = document.domain; var domain = det.replace('www.', '').split(/[/?#]/)[0]; const origin = btoa(d) - window.location.href = "https://" + domain + "/fetch/utils/?url=" + origin; - document.cookie = '__alloy_cookie_auth=yes; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; + window.location.href = "https://cdn." + domain + "/fetch/utils/?url=" + origin; + document.cookie = 'oldsmobile=badcar; expires=' + (Date.now() + 259200) + '; SameSite=Lax; domain=.' + auth + '; path=/; Secure;'; return false; }; diff --git a/public/index.html b/public/index.html index 7c95cded..5f6eaab2 100644 --- a/public/index.html +++ b/public/index.html @@ -7,6 +7,7 @@ H​oly Unb​loc​ke​r @@ -27,10 +28,12 @@ - + @@ -61,7 +64,8 @@ -->