{"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","NesTest","name","description","url","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","_ref","savedRomInfo","localStorage","getItem","existingLibrary","JSON","parse","added","Date","now","newRomInfo","stringify","concat","setItem","delete","removeItem","filter","ListPage","props","_this","Object","classCallCheck","possibleConstructorReturn","getPrototypeOf","call","deleteRom","updateLibrary","setState","romLibrary","handleDragOver","e","preventDefault","dataTransfer","dropEffect","handleDrop","items","getAsFile","files","history","push","pathname","state","_this2","react_default","a","createElement","className","onDragOver","onDrop","href","ListGroup","keys","sort","key","react_router_dom","to","encodeURIComponent","onClick","title","Component","ControlMapperRow","playerOneButton","playerTwoButton","waitingForKey","handleClick","bind","assertThisInitialized","button","playerButtons","console","log","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","KEYBOARD_ICON","ControlsModal","undefined","modified","handleKeyDown","handleGamepadButtonDown","listenForKey","controllerIcon","gamepadId","controllerIconAlt","setKeys","setGamepadConfig","removeKeyListener","promptButton","document","addEventListener","buttonInfo","playerId","slice","newConfig","assign","event","newKeys","objectSpread","defineProperty","keyCode","toConsumableArray","String","toUpperCase","removeEventListener","Modal","isOpen","toggle","ModalHeader","ModalBody","Table","src","alt","src_ControlMapperRow","Controller","BUTTON_LEFT","BUTTON_SELECT","BUTTON_RIGHT","BUTTON_UP","BUTTON_DOWN","BUTTON_A","BUTTON_B","BUTTON_START","ModalFooter","Button","outline","color","FPS","FrameTimer","onAnimationFrame","time","requestAnimationFrame","excess","interval","newFrameTime","lastFrameTime","numFrames","Math","round","generateFrame","onWriteFrame","timeToNextFrame","setTimeout","onGenerateFrame","running","_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","stop","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_WIDTH","SCREEN_HEIGHT","Screen","setBuffer","y","x","buf32","writeBuffer","imageData","data","set","buf8","context","putImageData","fitInParent","parent","canvas","parentNode","parentWidth","clientWidth","parentHeight","clientHeight","desiredRatio","style","width","height","handleMouseDown","onMouseDown","scale","parseFloat","rect","getBoundingClientRect","clientX","left","clientY","top","onMouseUp","ref","initCanvas","getContext","getImageData","fillStyle","fillRect","buf","Uint8ClampedArray","Uint32Array","img","Image","toDataURL","handleError","error","errorInfo","Raven","captureException","extra","Speakers","onBufferUnderrun","writeSample","right","size","bufferSize","deqN","enq","onaudioprocess","outputBuffer","getChannelData","samples","j","RingBuffer","AudioContext","myCtx","sampleRate","close","audioCtx","scriptNode","createScriptProcessor","connect","destination","disconnect","catch","Emulator","start","frameTimer","speakers","fpsInterval","setInterval","nes","getFPS","clearInterval","src_Screen_0","screen","frame","zapperMove","zapperFireDown","zapperFireUp","_this3","actualSize","desiredSize","paused","NES","onFrame","onStatusUpdate","onAudioSample","getSampleRate","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","Progress","position","src_Emulator","src_ControlsModal","abort","App","recordPageview","GoogleAnalytics","pageview","GOOGLE_ANALYTICS_CODE","initialize","react_router","exact","component","render","SENTRY_URI","install","ReactDOM","src_App_0","getElementById"],"mappings":"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","file":"static/js/main.d1ddc821.chunk.js","sourcesContent":["import React from \"react\";\n\nconst config = {\n ROMS: {\n NesTest: {\n name: \"LD44\",\n description: \"LD44 - The life of Cirkat\",\n url: \"http://cloud.weirdfox.com/GameJam/LD44.nes\"\n }\n }\n};\n\nexport default config;","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, Button } 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

JSNES

\n

\n A JavaScript NES emulator.{\" \"}\n Source on GitHub.\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 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","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\";\n\nimport \"./RunPage.css\";\n\nfunction 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\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
\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":""}