{"version":3,"sources":["config.js","RomLibrary.js","ListPage.js","ControlMapperRow.js","ControlsModal.js","FrameTimer.js","GamepadController.js","KeyboardController.js","Screen.js","utils.js","Speakers.js","Emulator.js","RunPage.js","App.js","index.js"],"names":["config","ROMS","corgssim","name","description","url","GOOGLE_ANALYTICS_CODE","process","REACT_APP_GOOGLE_ANALYTICS_CODE","SENTRY_URI","REACT_APP_SENTRY_URI","RomLibrary","getRomInfoByHash","hash","this","load","find","rom","save","file","Promise","resolve","reject","reader","FileReader","onload","readAsBinaryString","pFileReader","then","readFile","byteString","target","result","ab","ArrayBuffer","length","ia","Uint8Array","i","charCodeAt","crypto","subtle","digest","buffer","Array","from","map","b","toString","padStart","join","hashFile","savedRomInfo","localStorage","getItem","existingLibrary","JSON","parse","added","Date","now","newRomInfo","stringify","concat","setItem","delete","removeItem","filter","ListPage","Component","constructor","props","super","deleteRom","updateLibrary","setState","romLibrary","handleDragOver","e","preventDefault","dataTransfer","dropEffect","handleDrop","items","getAsFile","files","history","push","pathname","state","render","className","onDragOver","onDrop","href","ListGroup","Object","keys","sort","key","to","encodeURIComponent","a","onClick","title","ControlMapperRow","playerOneButton","playerTwoButton","waitingForKey","handleClick","bind","componentDidMount","button","playerButtons","console","log","componentDidUpdate","prevProps","prevState","gamepadButton","newButton","searchButton","gamepadConfig","buttonId","buttons","searchNewButton","prevGamepadConfig","some","b2","waitingForKeyPlayer","gamepadButtonName","type","code","value","playerGamepadId","configs","prevButton","newState","currentPromptButton","player","buttonName","GAMEPAD_ICON","ControlsModal","undefined","modified","handleKeyDown","handleGamepadButtonDown","listenForKey","controllerIcon","gamepadId","controllerIconAlt","componentWillUnmount","setKeys","setGamepadConfig","removeKeyListener","promptButton","document","addEventListener","buttonInfo","playerId","slice","newConfig","assign","event","newKeys","keyCode","String","toUpperCase","removeEventListener","Modal","isOpen","toggle","ModalHeader","ModalBody","Table","src","alt","Controller","BUTTON_LEFT","BUTTON_SELECT","BUTTON_RIGHT","BUTTON_UP","BUTTON_DOWN","BUTTON_A","BUTTON_B","BUTTON_START","ModalFooter","Button","outline","color","FrameTimer","onAnimationFrame","time","requestAnimationFrame","excess","interval","newFrameTime","lastFrameTime","numFrames","Math","round","generateFrame","onWriteFrame","timeToNextFrame","setTimeout","onGenerateFrame","running","start","stop","_requestID","window","cancelAnimationFrame","GamepadController","options","disableIfGamepadEnabled","callback","self","_getPlayerNumberFromGamepad","gamepad","id","poll","gamepads","navigator","getGamepads","webkitGetGamepads","usedPlayers","gamepadIndex","previousGamepad","gamepadState","previousButtons","buttonCallback","axes","axis","previousAxis","previousButton","pressed","playerNumber","indexOf","configButtons","configButton","onButtonDown","onButtonUp","f","loadGamepadConfig","startPolling","stopped","loop","KEYS","88","89","90","17","13","38","40","37","39","103","105","99","97","104","98","100","102","KeyboardController","loadKeys","handleKeyUp","handleKeyPress","Screen","setBuffer","y","x","buf32","writeBuffer","imageData","data","set","buf8","context","putImageData","fitInParent","parent","canvas","parentNode","parentWidth","clientWidth","parentHeight","clientHeight","style","width","height","handleMouseDown","onMouseDown","scale","parseFloat","rect","getBoundingClientRect","clientX","left","clientY","top","onMouseUp","ref","initCanvas","getContext","getImageData","fillStyle","fillRect","buf","Uint8ClampedArray","Uint32Array","screenshot","img","Image","toDataURL","handleError","error","errorInfo","Raven","captureException","extra","Speakers","onBufferUnderrun","writeSample","right","size","bufferSize","deqN","enq","onaudioprocess","outputBuffer","getChannelData","samples","j","RingBuffer","getSampleRate","AudioContext","myCtx","sampleRate","close","audioCtx","scriptNode","createScriptProcessor","connect","destination","disconnect","catch","Emulator","frameTimer","speakers","fpsInterval","setInterval","nes","getFPS","clearInterval","screen","frame","zapperMove","zapperFireDown","zapperFireUp","actualSize","desiredSize","paused","NES","onFrame","onStatusUpdate","onAudioSample","wrap","gamepadController","buttonDown","buttonUp","gamepadPolling","keyboardController","loadROM","romData","RunPage","match","params","slug","isLocalROM","test","romHash","split","romInfo","romName","localROMData","handleLoaded","currentRequest","path","handleProgress","req","XMLHttpRequest","open","overrideMimeType","status","responseText","Error","statusText","onerror","onprogress","send","loadBinary","err","message","location","lengthComputable","loadedPercent","loaded","total","loading","handlePauseResume","layout","navbarHeight","getComputedStyle","navbar","screenContainer","innerHeight","emulator","toggleControlsModal","controlsModalOpen","el","disabled","Progress","position","abort","App","recordPageview","GoogleAnalytics","pageview","initialize","exact","component","componentDidCatch","install","ReactDOM","getElementById"],"mappings":"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","file":"static/js/main.d6b7746e.chunk.js","sourcesContent":["import React from \"react\";\n\nconst config = {\n ROMS: {\n corgssim: {\n name: \"CORGS Simulator (U)\",\n description: \"A Retro Video Game Convention Simulator\",\n url: \"/roms/corgssim.nes\",\n },\n },\n GOOGLE_ANALYTICS_CODE: process.env.REACT_APP_GOOGLE_ANALYTICS_CODE,\n SENTRY_URI: process.env.REACT_APP_SENTRY_URI,\n};\n\nexport default config;\n","const pFileReader = function(file) {\n return new Promise((resolve, reject) => {\n var reader = new FileReader();\n reader.onload = resolve;\n reader.readAsBinaryString(file);\n });\n};\n\nconst hashFile = function(byteString) {\n const asHex = buffer => {\n return Array.from(new Uint8Array(buffer))\n .map(b => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n };\n\n const ab = new ArrayBuffer(byteString.length);\n var ia = new Uint8Array(ab);\n\n for (var i = 0; i < byteString.length; i++) {\n ia[i] = byteString.charCodeAt(i);\n }\n\n return crypto.subtle.digest(\"SHA-1\", ab).then(asHex);\n};\n\nconst RomLibrary = {\n getRomInfoByHash: function(hash) {\n return this.load().find(rom => rom.hash === hash);\n },\n save: function(file) {\n return pFileReader(file)\n .then(function(readFile) {\n const byteString = readFile.target.result;\n return hashFile(byteString).then(hash => {\n return { hash, byteString };\n });\n })\n .then(({ hash, byteString }) => {\n const savedRomInfo = localStorage.getItem(\"savedRomInfo\");\n const existingLibrary = savedRomInfo ? JSON.parse(savedRomInfo) : [];\n\n const rom = {\n name: file.name,\n hash: hash,\n added: Date.now()\n };\n\n const newRomInfo = JSON.stringify(existingLibrary.concat([rom]));\n\n localStorage.setItem(\"savedRomInfo\", newRomInfo);\n localStorage.setItem(\"blob-\" + hash, byteString);\n\n return rom;\n });\n },\n load: function() {\n const localData = localStorage.getItem(\"savedRomInfo\");\n if (!localData) return [];\n const savedRomInfo = JSON.parse(localStorage.getItem(\"savedRomInfo\"));\n return savedRomInfo || [];\n },\n delete: function(hash) {\n const existingLibrary = this.load();\n localStorage.removeItem(\"blob-\" + hash);\n localStorage.setItem(\n \"savedRomInfo\",\n JSON.stringify(existingLibrary.filter(rom => rom.hash !== hash))\n );\n }\n};\n\nexport default RomLibrary;\n","import React, { Component } from \"react\";\nimport \"./ListPage.css\";\nimport { ListGroup } from \"reactstrap\";\nimport { Link } from \"react-router-dom\";\nimport config from \"./config\";\n\nimport RomLibrary from \"./RomLibrary\";\n\nclass ListPage extends Component {\n constructor(props) {\n super(props);\n this.state = {\n romLibrary: RomLibrary.load(),\n };\n }\n render() {\n return (\n \n
\n
\n
\n
\n

JSNES

\n

\n A JavaScript NES emulator.{\" \"}\n \n Source on GitHub.\n \n

\n
\n\n \n {Object.keys(config.ROMS)\n .sort()\n .map((key) => (\n \n {config.ROMS[key][\"name\"]}\n \n \n ))}\n \n\n {this.state.romLibrary.length > 0 ? (\n
\n

Previously played:

\n\n \n {this.state.romLibrary\n .sort((a, b) => new Date(b.added) - new Date(a.added))\n .map((rom) => (\n \n {rom.name}\n {\n e.preventDefault();\n this.deleteRom(rom.hash);\n }}\n className=\"delete\"\n title=\"Delete\"\n >\n ×\n \n \n \n ))}\n \n
\n ) : null}\n
\n
\n
\n \n );\n }\n\n deleteRom = (hash) => {\n RomLibrary.delete(hash);\n this.updateLibrary();\n };\n\n updateLibrary = () => {\n this.setState({ romLibrary: RomLibrary.load() });\n };\n\n handleDragOver = (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"copy\";\n };\n\n handleDrop = (e) => {\n e.preventDefault();\n\n const file = e.dataTransfer.items\n ? e.dataTransfer.items[0].getAsFile()\n : e.dataTransfer.files[0];\n\n RomLibrary.save(file).then((rom) => {\n this.updateLibrary();\n this.props.history.push({ pathname: \"run/local-\" + rom.hash });\n });\n };\n}\n\nexport default ListPage;\n","import React, { Component } from \"react\";\n\nclass ControlMapperRow extends Component {\n constructor(props) {\n super(props);\n this.state = {\n playerOneButton: \"\",\n playerTwoButton: \"\",\n waitingForKey: 0\n };\n this.handleClick = this.handleClick.bind(this);\n }\n\n componentDidMount() {\n var keys = this.props.keys;\n var button = this.props.button;\n var playerButtons = [];\n for (var key in keys) {\n if (keys[key][0] === 1 && keys[key][1] === button) {\n playerButtons[0] = keys[key][2];\n console.log(playerButtons[0]);\n } else if (keys[key][0] === 2 && keys[key][1] === button) {\n playerButtons[1] = keys[key][2];\n }\n }\n this.setState({\n playerOneButton: playerButtons[0],\n playerTwoButton: playerButtons[1]\n });\n }\n\n componentDidUpdate(prevProps, prevState) {\n var keys = this.props.keys;\n var button = this.props.button;\n var playerButtons = [];\n var gamepadButton;\n var newButton;\n\n for (var key in keys) {\n if (keys[key][0] === 1 && keys[key][1] === button) {\n playerButtons[0] = keys[key][2];\n console.log(playerButtons[0]);\n } else if (keys[key][0] === 2 && keys[key][1] === button) {\n playerButtons[1] = keys[key][2];\n }\n }\n\n var searchButton = (gamepadConfig, buttonId) => {\n return gamepadConfig.buttons.filter(b => b.buttonId === buttonId)[0];\n };\n\n var searchNewButton = (prevGamepadConfig, gamepadConfig) => {\n return gamepadConfig.buttons.filter(b => {\n return (\n !prevGamepadConfig ||\n !prevGamepadConfig.buttons.some(b2 => b2.buttonId === b.buttonId)\n );\n })[0];\n };\n\n var waitingForKey = 0;\n var waitingForKeyPlayer = 0;\n\n var gamepadButtonName = gamepadButton => {\n if (gamepadButton.type === \"button\") return \"Btn-\" + gamepadButton.code;\n if (gamepadButton.type === \"axis\")\n return \"Axis-\" + gamepadButton.code + \" \" + gamepadButton.value;\n };\n\n if (this.props.gamepadConfig && this.props.gamepadConfig.playerGamepadId) {\n const playerGamepadId = this.props.gamepadConfig.playerGamepadId;\n if (playerGamepadId[0]) {\n playerButtons[0] = \"\";\n gamepadButton = searchButton(\n this.props.gamepadConfig.configs[playerGamepadId[0]],\n button\n );\n newButton = searchNewButton(\n prevProps.gamepadConfig.configs[playerGamepadId[0]],\n this.props.gamepadConfig.configs[playerGamepadId[0]]\n );\n if (gamepadButton) {\n playerButtons[0] = gamepadButtonName(gamepadButton);\n } else {\n if (newButton && newButton.buttonId === this.props.prevButton) {\n if (!waitingForKey) {\n waitingForKey = 1;\n waitingForKeyPlayer = 1;\n }\n }\n }\n }\n\n if (playerGamepadId[1]) {\n playerButtons[1] = \"\";\n gamepadButton = searchButton(\n this.props.gamepadConfig.configs[playerGamepadId[1]],\n button\n );\n newButton = searchNewButton(\n prevProps.gamepadConfig.configs[playerGamepadId[1]],\n this.props.gamepadConfig.configs[playerGamepadId[1]]\n );\n if (gamepadButton) {\n playerButtons[1] = gamepadButtonName(gamepadButton);\n } else {\n if (newButton && newButton.buttonId === this.props.prevButton) {\n if (!waitingForKey) {\n waitingForKey = 2;\n waitingForKeyPlayer = 2;\n }\n }\n }\n }\n }\n\n var newState = {};\n\n if (waitingForKey) {\n this.props.handleClick([waitingForKeyPlayer, this.props.button]);\n }\n // Prevent setState being called repeatedly\n if (\n prevState.playerOneButton !== playerButtons[0] ||\n prevState.playerTwoButton !== playerButtons[1]\n ) {\n newState.playerOneButton = playerButtons[0];\n newState.playerTwoButton = playerButtons[1];\n }\n\n if (waitingForKey) {\n newState.waitingForKey = waitingForKey;\n } else if (prevState.waitingForKey === 1) {\n if (this.props.currentPromptButton !== this.props.button) {\n newState.waitingForKey = 0;\n }\n } else if (prevState.waitingForKey === 2) {\n if (this.props.currentPromptButton !== this.props.button) {\n newState.waitingForKey = 0;\n }\n }\n\n if (Object.keys(newState).length > 0) {\n this.setState(newState);\n }\n }\n\n handleClick(player) {\n this.props.handleClick([player, this.props.button]);\n this.setState({\n waitingForKey: player\n });\n }\n\n render() {\n const waitingText = \"Press key or button...\";\n return (\n \n {this.props.buttonName}\n this.handleClick(1)}>\n {this.state.waitingForKey === 1\n ? waitingText\n : this.state.playerOneButton}\n \n this.handleClick(2)}>\n {this.state.waitingForKey === 2\n ? waitingText\n : this.state.playerTwoButton}\n \n \n );\n }\n}\n\nexport default ControlMapperRow;\n","import React, { Component } from \"react\";\nimport {\n Button,\n Modal,\n ModalHeader,\n ModalBody,\n ModalFooter,\n Table\n} from \"reactstrap\";\nimport { Controller } from \"jsnes\";\nimport ControlMapperRow from \"./ControlMapperRow\";\n\nconst GAMEPAD_ICON = \"../img/nes_controller.png\";\nconst KEYBOARD_ICON = \"../img/keyboard.png\";\n\nclass ControlsModal extends Component {\n constructor(props) {\n super(props);\n this.state = {\n gamepadConfig: props.gamepadConfig,\n keys: props.keys,\n button: undefined,\n modified: false\n };\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleGamepadButtonDown = this.handleGamepadButtonDown.bind(this);\n this.listenForKey = this.listenForKey.bind(this);\n\n this.state.gamepadConfig = this.state.gamepadConfig || {};\n this.state.gamepadConfig.playerGamepadId = this.state.gamepadConfig\n .playerGamepadId || [null, null];\n this.state.gamepadConfig.configs = this.state.gamepadConfig.configs || {};\n\n this.state.controllerIcon = this.state.gamepadConfig.playerGamepadId.map(\n gamepadId => (gamepadId ? GAMEPAD_ICON : KEYBOARD_ICON)\n );\n this.state.controllerIconAlt = this.state.gamepadConfig.playerGamepadId.map(\n gamepadId => (gamepadId ? \"gamepad\" : \"keyboard\")\n );\n this.state.currentPromptButton = -1;\n }\n\n componentWillUnmount() {\n if (this.state.modified) {\n this.props.setKeys(this.state.keys);\n this.props.setGamepadConfig(this.state.gamepadConfig);\n }\n this.removeKeyListener();\n }\n\n listenForKey(button) {\n var currentPromptButton = button[1];\n\n this.removeKeyListener();\n this.setState({ button, currentPromptButton });\n this.props.promptButton(this.handleGamepadButtonDown);\n document.addEventListener(\"keydown\", this.handleKeyDown);\n }\n\n handleGamepadButtonDown(buttonInfo) {\n this.removeKeyListener();\n\n var button = this.state.button;\n\n const playerId = button[0];\n const buttonId = button[1];\n\n const gamepadId = buttonInfo.gamepadId;\n const gamepadConfig = this.state.gamepadConfig;\n\n // link player to gamepad\n const playerGamepadId = gamepadConfig.playerGamepadId.slice(0);\n const newConfig = {};\n\n playerGamepadId[playerId - 1] = gamepadId;\n\n const rejectButtonId = b => {\n return b.buttonId !== buttonId;\n };\n\n const newButton = {\n code: buttonInfo.code,\n type: buttonInfo.type,\n buttonId: buttonId,\n value: buttonInfo.value\n };\n newConfig[gamepadId] = {\n buttons: (gamepadConfig.configs[gamepadId] || { buttons: [] }).buttons\n .filter(rejectButtonId)\n .concat([newButton])\n };\n\n const configs = Object.assign({}, gamepadConfig.configs, newConfig);\n\n this.setState({\n gamepadConfig: {\n configs: configs,\n playerGamepadId: playerGamepadId\n },\n currentPromptButton: -1,\n controllerIcon: playerGamepadId.map(gamepadId =>\n gamepadId ? GAMEPAD_ICON : KEYBOARD_ICON\n ),\n modified: true\n });\n }\n\n handleKeyDown(event) {\n this.removeKeyListener();\n\n var button = this.state.button;\n var keys = this.state.keys;\n var newKeys = {};\n for (var key in keys) {\n if (keys[key][0] !== button[0] || keys[key][1] !== button[1]) {\n newKeys[key] = keys[key];\n }\n }\n\n const playerGamepadId = this.state.gamepadConfig.playerGamepadId.slice(0);\n const playerId = button[0];\n playerGamepadId[playerId - 1] = null;\n\n this.setState({\n keys: {\n ...newKeys,\n [event.keyCode]: [\n ...button.slice(0, 2),\n event.key.length > 1 ? event.key : String(event.key).toUpperCase()\n ]\n },\n button: undefined,\n gamepadConfig: {\n configs: this.state.gamepadConfig.configs,\n playerGamepadId: playerGamepadId\n },\n currentPromptButton: -1,\n controllerIcon: playerGamepadId.map(gamepadId =>\n gamepadId ? GAMEPAD_ICON : KEYBOARD_ICON\n ),\n controllerIconAlt: playerGamepadId.map(gamepadId =>\n gamepadId ? \"gamepad\" : \"keyboard\"\n ),\n modified: true\n });\n }\n\n removeKeyListener() {\n this.props.promptButton(null);\n document.removeEventListener(\"keydown\", this.handleKeyDown);\n }\n\n render() {\n return (\n \n Controls\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Button\n Player 1\n \n \n Player 2\n \n
\n
\n \n \n \n \n );\n }\n}\n\nexport default ControlsModal;\n","const FPS = 60.098;\n\nexport default class FrameTimer {\n constructor(props) {\n // Run at 60 FPS\n this.onGenerateFrame = props.onGenerateFrame;\n // Run on animation frame\n this.onWriteFrame = props.onWriteFrame;\n this.onAnimationFrame = this.onAnimationFrame.bind(this);\n this.running = true;\n this.interval = 1e3 / FPS;\n this.lastFrameTime = false;\n }\n\n start() {\n this.running = true;\n this.requestAnimationFrame();\n }\n\n stop() {\n this.running = false;\n if (this._requestID) window.cancelAnimationFrame(this._requestID);\n this.lastFrameTime = false;\n }\n\n requestAnimationFrame() {\n this._requestID = window.requestAnimationFrame(this.onAnimationFrame);\n }\n\n generateFrame() {\n this.onGenerateFrame();\n this.lastFrameTime += this.interval;\n }\n\n onAnimationFrame = time => {\n this.requestAnimationFrame();\n // how many ms after 60fps frame time\n let excess = time % this.interval;\n\n // newFrameTime is the current time aligned to 60fps intervals.\n // i.e. 16.6, 33.3, etc ...\n let newFrameTime = time - excess;\n\n // first frame, do nothing\n if (!this.lastFrameTime) {\n this.lastFrameTime = newFrameTime;\n return;\n }\n\n let numFrames = Math.round(\n (newFrameTime - this.lastFrameTime) / this.interval\n );\n\n // This can happen a lot on a 144Hz display\n if (numFrames === 0) {\n //console.log(\"WOAH, no frames\");\n return;\n }\n\n // update display on first frame only\n this.generateFrame();\n this.onWriteFrame();\n\n // we generate additional frames evenly before the next\n // onAnimationFrame call.\n // additional frames are generated but not displayed\n // until next frame draw\n let timeToNextFrame = this.interval - excess;\n for (let i = 1; i < numFrames; i++) {\n setTimeout(() => {\n this.generateFrame();\n }, (i * timeToNextFrame) / numFrames);\n }\n if (numFrames > 1) console.log(\"SKIP\", numFrames - 1, this.lastFrameTime);\n };\n}\n","export default class GamepadController {\n constructor(options) {\n this.onButtonDown = options.onButtonDown;\n this.onButtonUp = options.onButtonUp;\n this.gamepadState = [];\n this.buttonCallback = null;\n }\n\n disableIfGamepadEnabled = callback => {\n var self = this;\n return (playerId, buttonId) => {\n if (!self.gamepadConfig) {\n return callback(playerId, buttonId);\n }\n\n var playerGamepadId = self.gamepadConfig.playerGamepadId;\n if (!playerGamepadId || !playerGamepadId[playerId - 1]) {\n // allow callback only if player is not associated to any gamepad\n return callback(playerId, buttonId);\n }\n };\n };\n\n _getPlayerNumberFromGamepad = gamepad => {\n if (this.gamepadConfig.playerGamepadId[0] === gamepad.id) {\n return 1;\n }\n\n if (this.gamepadConfig.playerGamepadId[1] === gamepad.id) {\n return 2;\n }\n\n return 1;\n };\n\n poll = () => {\n const gamepads = navigator.getGamepads\n ? navigator.getGamepads()\n : navigator.webkitGetGamepads();\n\n const usedPlayers = [];\n\n for (let gamepadIndex = 0; gamepadIndex < gamepads.length; gamepadIndex++) {\n const gamepad = gamepads[gamepadIndex];\n const previousGamepad = this.gamepadState[gamepadIndex];\n\n if (!gamepad) {\n continue;\n }\n\n if (!previousGamepad) {\n this.gamepadState[gamepadIndex] = gamepad;\n continue;\n }\n\n const buttons = gamepad.buttons;\n const previousButtons = previousGamepad.buttons;\n\n if (this.buttonCallback) {\n for (let code = 0; code < gamepad.axes.length; code++) {\n const axis = gamepad.axes[code];\n const previousAxis = previousGamepad.axes[code];\n\n if (axis === -1 && previousAxis !== -1) {\n this.buttonCallback({\n gamepadId: gamepad.id,\n type: \"axis\",\n code: code,\n value: axis\n });\n }\n\n if (axis === 1 && previousAxis !== 1) {\n this.buttonCallback({\n gamepadId: gamepad.id,\n type: \"axis\",\n code: code,\n value: axis\n });\n }\n }\n\n for (let code = 0; code < buttons.length; code++) {\n const button = buttons[code];\n const previousButton = previousButtons[code];\n if (button.pressed && !previousButton.pressed) {\n this.buttonCallback({\n gamepadId: gamepad.id,\n type: \"button\",\n code: code\n });\n }\n }\n } else if (this.gamepadConfig) {\n let playerNumber = this._getPlayerNumberFromGamepad(gamepad);\n if (usedPlayers.length < 2) {\n if (usedPlayers.indexOf(playerNumber) !== -1) {\n playerNumber++;\n if (playerNumber > 2) playerNumber = 1;\n }\n usedPlayers.push(playerNumber);\n\n if (this.gamepadConfig.configs[gamepad.id]) {\n const configButtons = this.gamepadConfig.configs[gamepad.id]\n .buttons;\n\n for (let i = 0; i < configButtons.length; i++) {\n const configButton = configButtons[i];\n if (configButton.type === \"button\") {\n const code = configButton.code;\n const button = buttons[code];\n const previousButton = previousButtons[code];\n\n if (button.pressed && !previousButton.pressed) {\n this.onButtonDown(playerNumber, configButton.buttonId);\n } else if (!button.pressed && previousButton.pressed) {\n this.onButtonUp(playerNumber, configButton.buttonId);\n }\n } else if (configButton.type === \"axis\") {\n const code = configButton.code;\n const axis = gamepad.axes[code];\n const previousAxis = previousGamepad.axes[code];\n\n if (\n axis === configButton.value &&\n previousAxis !== configButton.value\n ) {\n this.onButtonDown(playerNumber, configButton.buttonId);\n }\n\n if (\n axis !== configButton.value &&\n previousAxis === configButton.value\n ) {\n this.onButtonUp(playerNumber, configButton.buttonId);\n }\n }\n }\n }\n }\n }\n\n this.gamepadState[gamepadIndex] = {\n buttons: buttons.map(b => {\n return { pressed: b.pressed };\n }),\n axes: gamepad.axes.slice(0)\n };\n }\n };\n\n promptButton = f => {\n if (!f) {\n this.buttonCallback = f;\n } else {\n this.buttonCallback = buttonInfo => {\n this.buttonCallback = null;\n f(buttonInfo);\n };\n }\n };\n\n loadGamepadConfig = () => {\n var gamepadConfig;\n try {\n gamepadConfig = localStorage.getItem(\"gamepadConfig\");\n if (gamepadConfig) {\n gamepadConfig = JSON.parse(gamepadConfig);\n }\n } catch (e) {\n console.log(\"Failed to get gamepadConfig from localStorage.\", e);\n }\n\n this.gamepadConfig = gamepadConfig;\n };\n\n setGamepadConfig = gamepadConfig => {\n try {\n localStorage.setItem(\"gamepadConfig\", JSON.stringify(gamepadConfig));\n this.gamepadConfig = gamepadConfig;\n } catch (e) {\n console.log(\"Failed to set gamepadConfig in localStorage\");\n }\n };\n\n startPolling = () => {\n if (!(navigator.getGamepads || navigator.webkitGetGamepads)) {\n return { stop: () => {} };\n }\n\n let stopped = false;\n const loop = () => {\n if (stopped) return;\n\n this.poll();\n requestAnimationFrame(loop);\n };\n requestAnimationFrame(loop);\n\n return {\n stop: () => {\n stopped = true;\n }\n };\n };\n}\n","import { Controller } from \"jsnes\";\n\n// Mapping keyboard code to [controller, button]\nconst KEYS = {\n 88: [1, Controller.BUTTON_A, \"X\"], // X\n 89: [1, Controller.BUTTON_B, \"Y\"], // Y (Central European keyboard)\n 90: [1, Controller.BUTTON_B, \"Z\"], // Z\n 17: [1, Controller.BUTTON_SELECT, \"Right Ctrl\"], // Right Ctrl\n 13: [1, Controller.BUTTON_START, \"Enter\"], // Enter\n 38: [1, Controller.BUTTON_UP, \"Up\"], // Up\n 40: [1, Controller.BUTTON_DOWN, \"Down\"], // Down\n 37: [1, Controller.BUTTON_LEFT, \"Left\"], // Left\n 39: [1, Controller.BUTTON_RIGHT, \"Right\"], // Right\n 103: [2, Controller.BUTTON_A, \"Num-7\"], // Num-7\n 105: [2, Controller.BUTTON_B, \"Num-9\"], // Num-9\n 99: [2, Controller.BUTTON_SELECT, \"Num-3\"], // Num-3\n 97: [2, Controller.BUTTON_START, \"Num-1\"], // Num-1\n 104: [2, Controller.BUTTON_UP, \"Num-8\"], // Num-8\n 98: [2, Controller.BUTTON_DOWN, \"Num-2\"], // Num-2\n 100: [2, Controller.BUTTON_LEFT, \"Num-4\"], // Num-4\n 102: [2, Controller.BUTTON_RIGHT, \"Num-6\"] // Num-6\n};\n\nexport default class KeyboardController {\n constructor(options) {\n this.onButtonDown = options.onButtonDown;\n this.onButtonUp = options.onButtonUp;\n }\n\n loadKeys = () => {\n var keys;\n try {\n keys = localStorage.getItem(\"keys\");\n if (keys) {\n keys = JSON.parse(keys);\n }\n } catch (e) {\n console.log(\"Failed to get keys from localStorage.\", e);\n }\n\n this.keys = keys || KEYS;\n };\n\n setKeys = newKeys => {\n try {\n localStorage.setItem(\"keys\", JSON.stringify(newKeys));\n this.keys = newKeys;\n } catch (e) {\n console.log(\"Failed to set keys in localStorage\");\n }\n };\n\n handleKeyDown = e => {\n var key = this.keys[e.keyCode];\n if (key) {\n this.onButtonDown(key[0], key[1]);\n e.preventDefault();\n }\n };\n\n handleKeyUp = e => {\n var key = this.keys[e.keyCode];\n if (key) {\n this.onButtonUp(key[0], key[1]);\n e.preventDefault();\n }\n };\n\n handleKeyPress = e => {\n e.preventDefault();\n };\n}\n","import React, { Component } from \"react\";\nimport \"./Screen.css\";\n\nconst SCREEN_WIDTH = 256;\nconst SCREEN_HEIGHT = 240;\n\nclass Screen extends Component {\n render() {\n return (\n {\n this.canvas = canvas;\n }}\n />\n );\n }\n\n componentDidMount() {\n this.initCanvas();\n }\n\n componentDidUpdate() {\n this.initCanvas();\n }\n\n initCanvas() {\n this.context = this.canvas.getContext(\"2d\");\n this.imageData = this.context.getImageData(\n 0,\n 0,\n SCREEN_WIDTH,\n SCREEN_HEIGHT\n );\n\n this.context.fillStyle = \"black\";\n // set alpha to opaque\n this.context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);\n\n // buffer to write on next animation frame\n this.buf = new ArrayBuffer(this.imageData.data.length);\n // Get the canvas buffer in 8bit and 32bit\n this.buf8 = new Uint8ClampedArray(this.buf);\n this.buf32 = new Uint32Array(this.buf);\n\n // Set alpha\n for (var i = 0; i < this.buf32.length; ++i) {\n this.buf32[i] = 0xff000000;\n }\n }\n\n setBuffer = buffer => {\n var i = 0;\n for (var y = 0; y < SCREEN_HEIGHT; ++y) {\n for (var x = 0; x < SCREEN_WIDTH; ++x) {\n i = y * 256 + x;\n // Convert pixel from NES BGR to canvas ABGR\n this.buf32[i] = 0xff000000 | buffer[i]; // Full alpha\n }\n }\n };\n\n writeBuffer = () => {\n this.imageData.data.set(this.buf8);\n this.context.putImageData(this.imageData, 0, 0);\n };\n\n fitInParent = () => {\n let parent = this.canvas.parentNode;\n // @ts-ignore\n let parentWidth = parent.clientWidth;\n // @ts-ignore\n let parentHeight = parent.clientHeight;\n let parentRatio = parentWidth / parentHeight;\n let desiredRatio = SCREEN_WIDTH / SCREEN_HEIGHT;\n if (desiredRatio < parentRatio) {\n this.canvas.style.width = `${Math.round(parentHeight * desiredRatio)}px`;\n this.canvas.style.height = `${parentHeight}px`;\n } else {\n this.canvas.style.width = `${parentWidth}px`;\n this.canvas.style.height = `${Math.round(parentWidth / desiredRatio)}px`;\n }\n };\n\n screenshot() {\n var img = new Image();\n img.src = this.canvas.toDataURL(\"image/png\");\n return img;\n }\n\n handleMouseDown = e => {\n if (!this.props.onMouseDown) return;\n // Make coordinates unscaled\n let scale = SCREEN_WIDTH / parseFloat(this.canvas.style.width);\n let rect = this.canvas.getBoundingClientRect();\n let x = Math.round((e.clientX - rect.left) * scale);\n let y = Math.round((e.clientY - rect.top) * scale);\n this.props.onMouseDown(x, y);\n };\n}\n\nexport default Screen;\n","import Raven from \"raven-js\";\n\nexport const handleError = (error, errorInfo) => {\n console.error(error);\n Raven.captureException(error, { extra: errorInfo });\n};\n\nexport function loadBinary(path, callback, handleProgress) {\n var req = new XMLHttpRequest();\n req.open(\"GET\", path);\n req.overrideMimeType(\"text/plain; charset=x-user-defined\");\n req.onload = function() {\n if (this.status === 200) {\n if (req.responseText.match(/^/i)) {\n // Got HTML back, so it is probably falling back to index.html due to 404\n return callback(new Error(\"Page not found\"));\n }\n\n callback(null, this.responseText);\n } else if (this.status === 0) {\n // Aborted, so ignore error\n } else {\n callback(new Error(req.statusText));\n }\n };\n req.onerror = function() {\n callback(new Error(req.statusText));\n };\n req.onprogress = handleProgress;\n req.send();\n return req;\n}\n","import RingBuffer from \"ringbufferjs\";\nimport { handleError } from \"./utils\";\n\nexport default class Speakers {\n constructor({ onBufferUnderrun }) {\n this.onBufferUnderrun = onBufferUnderrun;\n this.bufferSize = 8192;\n this.buffer = new RingBuffer(this.bufferSize * 2);\n }\n\n getSampleRate() {\n if (!window.AudioContext) {\n return 44100;\n }\n let myCtx = new window.AudioContext();\n let sampleRate = myCtx.sampleRate;\n myCtx.close();\n return sampleRate;\n }\n\n start() {\n // Audio is not supported\n if (!window.AudioContext) {\n return;\n }\n this.audioCtx = new window.AudioContext();\n this.scriptNode = this.audioCtx.createScriptProcessor(1024, 0, 2);\n this.scriptNode.onaudioprocess = this.onaudioprocess;\n this.scriptNode.connect(this.audioCtx.destination);\n }\n\n stop() {\n if (this.scriptNode) {\n this.scriptNode.disconnect(this.audioCtx.destination);\n this.scriptNode.onaudioprocess = null;\n this.scriptNode = null;\n }\n if (this.audioCtx) {\n this.audioCtx.close().catch(handleError);\n this.audioCtx = null;\n }\n }\n\n writeSample = (left, right) => {\n if (this.buffer.size() / 2 >= this.bufferSize) {\n console.log(`Buffer overrun`);\n this.buffer.deqN(this.bufferSize / 2);\n }\n this.buffer.enq(left);\n this.buffer.enq(right);\n };\n\n onaudioprocess = e => {\n var left = e.outputBuffer.getChannelData(0);\n var right = e.outputBuffer.getChannelData(1);\n var size = left.length;\n\n // We're going to buffer underrun. Attempt to fill the buffer.\n if (this.buffer.size() < size * 2 && this.onBufferUnderrun) {\n this.onBufferUnderrun(this.buffer.size(), size * 2);\n }\n\n try {\n var samples = this.buffer.deqN(size * 2);\n } catch (e) {\n // onBufferUnderrun failed to fill the buffer, so handle a real buffer\n // underrun\n\n // ignore empty buffers... assume audio has just stopped\n var bufferSize = this.buffer.size() / 2;\n if (bufferSize > 0) {\n console.log(`Buffer underrun (needed ${size}, got ${bufferSize})`);\n }\n for (var j = 0; j < size; j++) {\n left[j] = 0;\n right[j] = 0;\n }\n return;\n }\n for (var i = 0; i < size; i++) {\n left[i] = samples[i * 2];\n right[i] = samples[i * 2 + 1];\n }\n };\n}\n","import Raven from \"raven-js\";\nimport React, { Component } from \"react\";\nimport PropTypes from \"prop-types\";\nimport { NES } from \"jsnes\";\n\nimport FrameTimer from \"./FrameTimer\";\nimport GamepadController from \"./GamepadController\";\nimport KeyboardController from \"./KeyboardController\";\nimport Screen from \"./Screen\";\nimport Speakers from \"./Speakers\";\n\n/*\n * Runs the emulator.\n *\n * The only UI is a canvas element. It assumes it is a singleton in various ways\n * (binds to window, keyboard, speakers, etc).\n */\nclass Emulator extends Component {\n render() {\n return (\n {\n this.screen = screen;\n }}\n onGenerateFrame={() => {\n this.nes.frame();\n }}\n onMouseDown={(x, y) => {\n this.nes.zapperMove(x, y);\n this.nes.zapperFireDown();\n }}\n onMouseUp={() => {\n this.nes.zapperFireUp();\n }}\n />\n );\n }\n\n componentDidMount() {\n // Initial layout\n this.fitInParent();\n\n this.speakers = new Speakers({\n onBufferUnderrun: (actualSize, desiredSize) => {\n if (this.props.paused) {\n return;\n }\n // Skip a video frame so audio remains consistent. This happens for\n // a variety of reasons:\n // - Frame rate is not quite 60fps, so sometimes buffer empties\n // - Page is not visible, so requestAnimationFrame doesn't get fired.\n // In this case emulator still runs at full speed, but timing is\n // done by audio instead of requestAnimationFrame.\n // - System can't run emulator at full speed. In this case it'll stop\n // firing requestAnimationFrame.\n console.log(\n \"Buffer underrun, running another frame to try and catch up\"\n );\n\n this.frameTimer.generateFrame();\n // desiredSize will be 2048, and the NES produces 1468 samples on each\n // frame so we might need a second frame to be run. Give up after that\n // though -- the system is not catching up\n if (this.speakers.buffer.size() < desiredSize) {\n console.log(\"Still buffer underrun, running a second frame\");\n this.frameTimer.generateFrame();\n }\n }\n });\n\n this.nes = new NES({\n onFrame: this.screen.setBuffer,\n onStatusUpdate: console.log,\n onAudioSample: this.speakers.writeSample,\n sampleRate: this.speakers.getSampleRate()\n });\n\n // For debugging. ([\"nes\"] instead of .nes to avoid VS Code type errors.)\n window[\"nes\"] = this.nes;\n\n this.frameTimer = new FrameTimer({\n onGenerateFrame: Raven.wrap(this.nes.frame),\n onWriteFrame: Raven.wrap(this.screen.writeBuffer)\n });\n\n // Set up gamepad and keyboard\n this.gamepadController = new GamepadController({\n onButtonDown: this.nes.buttonDown,\n onButtonUp: this.nes.buttonUp\n });\n\n this.gamepadController.loadGamepadConfig();\n this.gamepadPolling = this.gamepadController.startPolling();\n\n this.keyboardController = new KeyboardController({\n onButtonDown: this.gamepadController.disableIfGamepadEnabled(\n this.nes.buttonDown\n ),\n onButtonUp: this.gamepadController.disableIfGamepadEnabled(\n this.nes.buttonUp\n )\n });\n\n // Load keys from localStorage (if they exist)\n this.keyboardController.loadKeys();\n\n document.addEventListener(\"keydown\", this.keyboardController.handleKeyDown);\n document.addEventListener(\"keyup\", this.keyboardController.handleKeyUp);\n document.addEventListener(\n \"keypress\",\n this.keyboardController.handleKeyPress\n );\n\n this.nes.loadROM(this.props.romData);\n this.start();\n }\n\n componentWillUnmount() {\n this.stop();\n\n // Unbind keyboard\n document.removeEventListener(\n \"keydown\",\n this.keyboardController.handleKeyDown\n );\n document.removeEventListener(\"keyup\", this.keyboardController.handleKeyUp);\n document.removeEventListener(\n \"keypress\",\n this.keyboardController.handleKeyPress\n );\n\n // Stop gamepad\n this.gamepadPolling.stop();\n\n window[\"nes\"] = undefined;\n }\n\n componentDidUpdate(prevProps) {\n if (this.props.paused !== prevProps.paused) {\n if (this.props.paused) {\n this.stop();\n } else {\n this.start();\n }\n }\n\n // TODO: handle changing romData\n }\n\n start = () => {\n this.frameTimer.start();\n this.speakers.start();\n this.fpsInterval = setInterval(() => {\n console.log(`FPS: ${this.nes.getFPS()}`);\n }, 1000);\n };\n\n stop = () => {\n this.frameTimer.stop();\n this.speakers.stop();\n clearInterval(this.fpsInterval);\n };\n\n /*\n * Fill parent element with screen. Typically called if parent element changes size.\n */\n fitInParent() {\n this.screen.fitInParent();\n }\n}\n\nEmulator.propTypes = {\n paused: PropTypes.bool,\n romData: PropTypes.string.isRequired\n};\n\nexport default Emulator;\n","import React, { Component } from \"react\";\nimport { Button, Progress } from \"reactstrap\";\nimport { Link } from \"react-router-dom\";\n\nimport config from \"./config\";\nimport ControlsModal from \"./ControlsModal\";\nimport Emulator from \"./Emulator\";\nimport RomLibrary from \"./RomLibrary\";\nimport { loadBinary } from \"./utils\";\n\nimport \"./RunPage.css\";\n\n/*\n * The UI for the emulator. Also responsible for loading ROM from URL or file.\n */\nclass RunPage extends Component {\n constructor(props) {\n super(props);\n this.state = {\n romName: null,\n romData: null,\n running: false,\n paused: false,\n controlsModalOpen: false,\n loading: true,\n loadedPercent: 3,\n error: null\n };\n }\n\n render() {\n return (\n
\n {\n this.navbar = el;\n }}\n >\n
    \n
  • \n \n ‹ Back\n \n
  • \n
\n
    \n
  • \n {this.state.romName}\n
  • \n
\n
    \n
  • \n \n Controls\n \n \n {this.state.paused ? \"Resume\" : \"Pause\"}\n \n
  • \n
\n \n\n {this.state.error ? (\n this.state.error\n ) : (\n {\n this.screenContainer = el;\n }}\n >\n {this.state.loading ? (\n \n ) : this.state.romData ? (\n {\n this.emulator = emulator;\n }}\n />\n ) : null}\n\n {/* TODO: lift keyboard and gamepad state up */}\n {this.state.controlsModalOpen && (\n \n )}\n
\n )}\n \n );\n }\n\n componentDidMount() {\n window.addEventListener(\"resize\", this.layout);\n this.layout();\n this.load();\n }\n\n componentWillUnmount() {\n window.removeEventListener(\"resize\", this.layout);\n if (this.currentRequest) {\n this.currentRequest.abort();\n }\n }\n\n load = () => {\n if (this.props.match.params.slug) {\n const slug = this.props.match.params.slug;\n const isLocalROM = /^local-/.test(slug);\n const romHash = slug.split(\"-\")[1];\n const romInfo = isLocalROM\n ? RomLibrary.getRomInfoByHash(romHash)\n : config.ROMS[slug];\n\n if (!romInfo) {\n this.setState({ error: `No such ROM: ${slug}` });\n return;\n }\n\n if (isLocalROM) {\n this.setState({ romName: romInfo.name });\n const localROMData = localStorage.getItem(\"blob-\" + romHash);\n this.handleLoaded(localROMData);\n } else {\n this.setState({ romName: romInfo.description });\n this.currentRequest = loadBinary(\n romInfo.url,\n (err, data) => {\n if (err) {\n this.setState({ error: `Error loading ROM: ${err.message}` });\n } else {\n this.handleLoaded(data);\n }\n },\n this.handleProgress\n );\n }\n } else if (this.props.location.state && this.props.location.state.file) {\n let reader = new FileReader();\n reader.readAsBinaryString(this.props.location.state.file);\n reader.onload = e => {\n this.currentRequest = null;\n this.handleLoaded(reader.result);\n };\n } else {\n this.setState({ error: \"No ROM provided\" });\n }\n };\n\n handleProgress = e => {\n if (e.lengthComputable) {\n this.setState({ loadedPercent: (e.loaded / e.total) * 100 });\n }\n };\n\n handleLoaded = data => {\n this.setState({ running: true, loading: false, romData: data });\n };\n\n handlePauseResume = () => {\n this.setState({ paused: !this.state.paused });\n };\n\n layout = () => {\n let navbarHeight = parseFloat(window.getComputedStyle(this.navbar).height);\n this.screenContainer.style.height = `${window.innerHeight -\n navbarHeight}px`;\n if (this.emulator) {\n this.emulator.fitInParent();\n }\n };\n\n toggleControlsModal = () => {\n this.setState({ controlsModalOpen: !this.state.controlsModalOpen });\n };\n}\n\nexport default RunPage;\n","import React, { Component } from \"react\";\nimport GoogleAnalytics from \"react-ga\";\nimport { BrowserRouter, Route } from \"react-router-dom\";\nimport ListPage from \"./ListPage\";\nimport RunPage from \"./RunPage\";\nimport config from \"./config\";\nimport { handleError } from \"./utils\";\nimport \"./App.css\";\n\nclass App extends Component {\n constructor(props) {\n super(props);\n this.state = { error: null };\n if (config.GOOGLE_ANALYTICS_CODE) {\n GoogleAnalytics.initialize(config.GOOGLE_ANALYTICS_CODE);\n }\n }\n\n render() {\n if (this.state.error) {\n return (\n
\n
\n
\n Oops - there has been an error. It has been logged to the console.\n
\n
\n
\n );\n }\n return (\n \n
\n \n \n \n \n
\n
\n );\n }\n\n componentDidCatch(error, errorInfo) {\n this.setState({ error });\n handleError(error, errorInfo);\n }\n\n recordPageview = ({ location }) => {\n GoogleAnalytics.pageview(location.pathname);\n return null;\n };\n}\n\nexport default App;\n","import Raven from \"raven-js\";\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport App from \"./App\";\nimport config from \"./config\";\nimport \"./index.scss\";\n\nif (config.SENTRY_URI) {\n Raven.config(config.SENTRY_URI).install();\n}\n\nRaven.context(function() {\n ReactDOM.render(, document.getElementById(\"root\"));\n});\n"],"sourceRoot":""}